{"id":1423,"date":"2023-10-10T13:33:49","date_gmt":"2023-10-10T12:33:49","guid":{"rendered":"https:\/\/exponentialdecay.co.uk\/blog\/?p=1423"},"modified":"2025-06-26T19:53:36","modified_gmt":"2025-06-26T19:53:36","slug":"linting-as-understanding","status":"publish","type":"post","link":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/","title":{"rendered":"Linting as understanding"},"content":{"rendered":"<p>I have been working on a Python template repository as part of my day-job at <a href=\"https:\/\/orcfax.io\/\" target=\"_blank\" rel=\"noopener\">Orcfax<\/a>.<\/p>\n<p>It is based on the popular pypa <a href=\"https:\/\/github.com\/pypa\/sampleproject\" target=\"_blank\" rel=\"noopener\">sample project<\/a> and adds important tooling that supports the quality assurance of projects that many developers are expected to engage with.<\/p>\n<p>In my template repository I add editor defaults, linting, and prepare the repository for unit tests, and then deployment.<\/p>\n<p>I have migrated a copy of the template I created for Orcfax to a new file format organisation I have created to capture work I am doing around tools such as <a href=\"https:\/\/ffdev.info\" target=\"_blank\" rel=\"noopener\">ffdev.info<\/a> (the PRONOM signature development utility).<\/p>\n<p>The new template repository can be found here: <a href=\"https:\/\/github.com\/ffdev-info\/template.py\" target=\"_blank\" rel=\"noopener\">ffdev-info\/template.py<\/a>.<\/p>\n<p>I want to talk about how this tooling can be used as a way of understanding legacy, or new code that you are going to be looking at. Looking at how linting can be useful for learning and understanding.<\/p>\n<p><!--more--><\/p>\n<h2>Linting as understanding<\/h2>\n<p>The title of this blog came from my introduction to a new piece of code. I wanted to see if I could use the script, but the code was a bit out of date and didn&#8217;t use strong Python coding standards &#8211; such as those captured in the famous <a href=\"https:\/\/peps.python.org\/pep-0008\/\" target=\"_blank\" rel=\"noopener\">Python PEP-8 guidelines<\/a>.<\/p>\n<p>I also ran into a similar thing just last week when I converted the code supporting <a href=\"https:\/\/exponentialdecay.co.uk\/blog\/genesis-of-a-file-format\/\" target=\"_blank\" rel=\"noopener\">Genesis of a File Format<\/a> into Python 3 from Python 2.<\/p>\n<p>In both cases I needed help improving:<\/p>\n<ul>\n<li>readability,<\/li>\n<li>introducing more idiomatic patterns to the code-base (also increasing readability),<\/li>\n<li>deciphering parts of the code that weren&#8217;t immediately clear such as redundant flow of execution.<\/li>\n<\/ul>\n<p>Linting is a process that can help with all three of these things. Linting refers to a process of &#8220;static analysis&#8221; that identifies programming errors, bugs, stylistic errors and (according to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Lint_(software)\" target=\"_blank\" rel=\"noopener\">Wikipedia<\/a>) suspicious constructs; I&#8217;d re-frame this as under-optimized code-layout, but it is true, linting can also identify potential safety and security concerns.<\/p>\n<p>Some linting tools can fix code in-place, others require human interaction. Both have their benefits that can help us to understand a new codebase better.<\/p>\n<h3>Adding a touch of lint<\/h3>\n<p>I took the linting components of the template repository and copy and pasted them into the unfamiliar codebase and started to understand the project through its linting output.<\/p>\n<p>The <a href=\"https:\/\/github.com\/ross-spencer\/eyeglass\" target=\"_blank\" rel=\"noopener\">eyeglass repository<\/a> is a good example for a very flat project, i.e. a project that isn&#8217;t expected to be packaged, and likely to be run as a standalone script.<\/p>\n<p>Its layout before adding linting:<\/p>\n<pre>\u251c\u2500\u2500 default-sample-1.0-be.eygl\r\n\u251c\u2500\u2500 eyeglass-default.py\r\n\u251c\u2500\u2500 eyeglass.py\r\n\u251c\u2500\u2500 eyeglass-signature-sample-files\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 eyeglass-big-endian.eygl\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 eyeglass-bof-eof.eygl\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 eyeglass-characterisation-signature-file.xml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 eyeglass-complete-signature-file-DROID-6.0-only.xml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 eyeglass-complete-signature-file.xml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 eyeglass-id-signature-file.xml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 eyeglass-invalid-endianness.eygl\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 eyeglass-little-endian.eygl\r\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 eyeglass-no-eof.eygl\r\n\u251c\u2500\u2500 prescription-sample-1.0-be.eygl\r\n\u251c\u2500\u2500 prescription-sample-1.0-le.eygl\r\n\u2514\u2500\u2500 README.md\r\n<\/pre>\n<p>And the files I added:<\/p>\n<pre>\u251c\u2500\u2500 .codespellrc\r\n\u251c\u2500\u2500 .editorconfig\r\n\u251c\u2500\u2500 .gitignore\r\n\u251c\u2500\u2500 .markdownlint.yaml\r\n\u251c\u2500\u2500 .pre-commit-config.yaml\r\n\u251c\u2500\u2500 .pylintrc\r\n\u251c\u2500\u2500 pytest.ini\r\n\u251c\u2500\u2500 requirements\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 local.txt\r\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 requirements.txt\r\n\u251c\u2500\u2500 .ruff.toml\r\n\u251c\u2500\u2500 tox.ini\r\n\u2514\u2500\u2500 .vscode\r\n\u2514\u2500\u2500 settings.json\r\n<\/pre>\n<h3>Breaking down the changes<\/h3>\n<p>The files added can be summarized as follows. It&#8217;s a bit opaque to begin with but I will try to go into more useful detail. I also recommend taking a look at the different links in-line for more information.<\/p>\n<ul>\n<li>We add <a href=\"https:\/\/tox.wiki\/en\/4.11.3\/config.html\" target=\"_blank\" rel=\"noopener\"><code><span style=\"text-decoration: underline;\">tox.ini<\/span><\/code><\/a> and <code><a href=\"https:\/\/docs.pytest.org\/en\/stable\/reference\/customize.html\" target=\"_blank\" rel=\"noopener\">pytest.ini<\/a><\/code> to allow us to run linting and test processes.<\/li>\n<li><code><a href=\"https:\/\/pre-commit.com\/\" target=\"_blank\" rel=\"noopener\">.pre-commit-config.yaml<\/a><\/code> allows us to configure a runner for other linting processes.<\/li>\n<li><code><a href=\"https:\/\/git-scm.com\/docs\/gitignore\" target=\"_blank\" rel=\"noopener\">.gitignore<\/a><\/code> allows us to ignore artifacts from linting or testing that we don&#8217;t want to commit to source control.<\/li>\n<li><code>requirements\/local.txt<\/code> and <code>requirements\/requirements.txt<\/code> allow us to install linting dependencies.<\/li>\n<li><code><a href=\"https:\/\/pypi.org\/project\/codespell\/\" target=\"_blank\" rel=\"noopener\">.codespellrc<\/a><\/code>, <code><a href=\"https:\/\/github.com\/igorshubovych\/markdownlint-cli#configuration\" target=\"_blank\" rel=\"noopener\">.markdownlint.yaml<\/a><\/code>, <a href=\"https:\/\/pylint.pycqa.org\/en\/latest\/user_guide\/usage\/run.html\" target=\"_blank\" rel=\"noopener\"><code><span style=\"text-decoration: underline;\">.pylintrc<\/span><\/code><\/a>, <code><a href=\"https:\/\/docs.astral.sh\/ruff\/configuration\/\" target=\"_blank\" rel=\"noopener\">.ruff.toml<\/a><\/code> are configuration files for some of the more opinionated tooling we are adding.<\/li>\n<li><code><a href=\"https:\/\/editorconfig.org\/\" target=\"_blank\" rel=\"noopener\">.editorconfig<\/a><\/code> and <span style=\"text-decoration: underline;\"><code><a href=\"https:\/\/code.visualstudio.com\/docs\/getstarted\/settings\" target=\"_blank\" rel=\"noopener\">.vscode\/settings.json<\/a><\/code><\/span> help us to configure our code editors consistently so that our settings do not change existing code unexpectedly.<\/li>\n<\/ul>\n<p>All in all, those files enable us to run all of the following:<\/p>\n<ul>\n<li><a href=\"https:\/\/pre-commit.com\/hooks.html\" target=\"_blank\" rel=\"noopener\">pre-commit: check-yaml<\/a> &#8211; checks yaml files for parseable syntax.<\/li>\n<li><a href=\"https:\/\/pre-commit.com\/hooks.html\" target=\"_blank\" rel=\"noopener\">pre-commit: check-json<\/a> &#8211; checks json files for parseable syntax.<\/li>\n<li><a href=\"https:\/\/pre-commit.com\/hooks.html\" target=\"_blank\" rel=\"noopener\">pre-commit: check-toml<\/a> &#8211; checks toml files for parseable syntax.<\/li>\n<li><a href=\"https:\/\/pre-commit.com\/hooks.html\" target=\"_blank\" rel=\"noopener\">pre-commit: end-of-file-fixer<\/a> &#8211; ensures that a file is either empty, or ends with one newline.<\/li>\n<li><a href=\"https:\/\/pre-commit.com\/hooks.html\" target=\"_blank\" rel=\"noopener\">pre-commit: trailing-whitespace<\/a> &#8211; trims trailing whitespace.<\/li>\n<li><a href=\"https:\/\/pre-commit.com\/hooks.html\" target=\"_blank\" rel=\"noopener\">pre-commit: check-case-conflict<\/a> &#8211; checks for files that would conflict in case-insensitive filesystems.<\/li>\n<li><a href=\"https:\/\/github.com\/psf\/black\" target=\"_blank\" rel=\"noopener\">psf\/black<\/a> &#8211; Python code (layout) formatter.<\/li>\n<li><a href=\"https:\/\/github.com\/PyCQA\/isort\" target=\"_blank\" rel=\"noopener\">pycqa\/isort<\/a> &#8211; sorts Python imports idiomatically (<em>&#8220;isort your imports, so you don&#8217;t have to.&#8221;<\/em>)<\/li>\n<li><a href=\"https:\/\/github.com\/astral-sh\/ruff\" target=\"_blank\" rel=\"noopener\">astral-sh\/ruff<\/a> &#8211; Python linter, written in Rust.<\/li>\n<li><a href=\"https:\/\/github.com\/igorshubovych\/markdownlint-cli#configuration\" target=\"_blank\" rel=\"noopener\">igorshuovych\/markdownlint-cli<\/a> &#8211; check markdown files and flag style issues.<\/li>\n<li><a href=\"https:\/\/github.com\/codespell-project\/codespell\" target=\"_blank\" rel=\"noopener\">codespell-project\/codespell<\/a> &#8211; checks code for common misspellings.<\/li>\n<\/ul>\n<h3>Installation and running<\/h3>\n<p>Installation can be done as follows:<\/p>\n<pre>python3 -m venv venv\r\nsource venv\/bin\/activate\r\npython -m pip install -r requirements\/local.txt\r\n<\/pre>\n<p>To run:<\/p>\n<pre>python -m tox -e linting\r\n<\/pre>\n<p>Depending on the project, the output of the tools running for the first time will vary. The eyeglass project looked as follows:<\/p>\n<p><code>check yaml...............................................................Passed<\/code><br \/>\n<code>check json...............................................................Passed<\/code><br \/>\n<code>check toml...............................................................Passed<\/code><br \/>\n<code>fix end of files.........................................................Failed<\/code><br \/>\n<code>- hook id: end-of-file-fixer<\/code><br \/>\n<code>- exit code: 1<\/code><br \/>\n<code>- files were modified by this hook<\/code><\/p>\n<p><code>Fixing eyeglass_default.py<\/code><br \/>\n<code>Fixing eyeglass-signature-sample-files\/eyeglass-characterisation-signature-file.xml<\/code><br \/>\n<code>Fixing eyeglass.py<\/code><br \/>\n<code>Fixing eyeglass-signature-sample-files\/eyeglass-id-signature-file.xml<\/code><br \/>\n<code>Fixing eyeglass-signature-sample-files\/eyeglass-complete-signature-file.xml<\/code><br \/>\n<code>Fixing README.md<\/code><br \/>\n<code>Fixing eyeglass-signature-sample-files\/eyeglass-complete-signature-file-DROID-6.0-only.xml<\/code><\/p>\n<p><code>trim trailing whitespace.................................................Failed<\/code><br \/>\n<code>- hook id: trailing-whitespace<\/code><br \/>\n<code>- exit code: 1<\/code><br \/>\n<code>- files were modified by this hook<\/code><\/p>\n<p><code>Fixing eyeglass_default.py<\/code><br \/>\n<code>Fixing eyeglass.py<\/code><br \/>\n<code>Fixing README.md<\/code><\/p>\n<p><code>check for case conflicts.................................................Passed<\/code><br \/>\n<code>black....................................................................Failed<\/code><br \/>\n<code>- hook id: black<\/code><br \/>\n<code>- files were modified by this hook<\/code><\/p>\n<p><code>reformatted eyeglass_default.py<\/code><br \/>\n<code>reformatted eyeglass.py<\/code><\/p>\n<p><code>All done! \u2728 ???? \u2728<\/code><br \/>\n<code>2 files reformatted.<\/code><\/p>\n<p><code>isort....................................................................Passed<\/code><br \/>\n<code>ruff.....................................................................Passed<\/code><br \/>\n<code>markdownlint.............................................................Failed<\/code><br \/>\n<code>- hook id: markdownlint<\/code><br \/>\n<code>- exit code: 1<\/code><\/p>\n<p><code>README.md:4:81 MD013\/line-length Line length [Expected: 80; Actual: 195]<\/code><br \/>\n<code>README.md:6:1 MD018\/no-missing-space-atx No space after hash on atx style heading [Context: \"###Specification\"]<\/code><br \/>\n<code>README.md:35:1 MD018\/no-missing-space-atx No space after hash on atx style heading [Context: \"###Further reading\"]<\/code><br \/>\n<code>README.md:37:1 MD034\/no-bare-urls Bare URL used [Context: \"http:\/\/exponentialdecay.co.uk\/...\"]<\/code><\/p>\n<p><code>codespell................................................................Passed<\/code><br \/>\n<code>pylint...................................................................Failed<\/code><br \/>\n<code>- hook id: pylint<\/code><br \/>\n<code>- exit code: 28<\/code><\/p>\n<p><code>************* Module eyeglass_default<\/code><br \/>\n<code>eyeglass_default.py:1:0: C0114: Missing module docstring (missing-module-docstring)<\/code><br \/>\n<code>************* Module eyeglass<\/code><br \/>\n<code>eyeglass.py:1:0: C0114: Missing module docstring (missing-module-docstring)<\/code><br \/>\n<code>eyeglass.py:10:0: R0902: Too many instance attributes (20\/7) (too-many-instance-attributes)<\/code><br \/>\n<code>eyeglass.py:54:8: C0103: Variable name \"d\" doesn't conform to snake_case naming style (invalid-name)<\/code><br \/>\n<code>eyeglass.py:75:15: R1732: Consider using 'with' for resource-allocating operations (consider-using-with)<\/code><br \/>\n<code>eyeglass.py:127:4: C0116: Missing function or method docstring (missing-function-docstring)<\/code><br \/>\n<code>eyeglass.py:131:4: C0116: Missing function or method docstring (missing-function-docstring)<\/code><br \/>\n<code>...<\/code><code>eyeglass.py:192:4: C0116: Missing function or method docstring (missing-function-docstring)<\/code><br \/>\n<code>eyeglass.py:60:8: W0201: Attribute 'bigendian' defined outside __init__ (attribute-defined-outside-init)<\/code><br \/>\n<code>eyeglass.py:62:12: W0201: Attribute 'float' defined outside __init__ (attribute-defined-outside-init)<\/code><br \/>\n<code>...<\/code><code>eyeglass.py:70:12: W0201: Attribute 'int' defined outside __init__ (attribute-defined-outside-init)<\/code><\/p>\n<p>Given an output like this, I would then start to work through the output and fix the issues. It may not be immediately clear how this helps my understanding, so I will elaborate a bit more below.<\/p>\n<h2>Linting Highlights<\/h2>\n<h3>Automated fixes<\/h3>\n<p>Automated fixes help to create an idiomatic view of code across distributed environments, e.g. when working on code with bigger teams, or even just moving code between two computers.<\/p>\n<p>Black is an exceptional tool here and the end result for a user is that they can cast their eye over black formatted code and understand its shape with greater ease than if the code remained unformatted, i.e. as was perhaps first drafted.<\/p>\n<h3>Project settings<\/h3>\n<p>Opinionated project settings also provide automated fixes. These tend to be as the developer writes code for the first time. The most important setting I have seen reduce friction on a project is the removal of whitespace at the end of individual lines of code. These are often added accidentally when typing or when browsing a codebase. The problem with newlines at the end of code, however, is that they can impact the output of a &#8220;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Diff\" target=\"_blank\" rel=\"noopener\">diff<\/a>&#8221; (an important tool used in code review to view changes, differences, or &#8220;diffs&#8221;) as they compare code line-by-line. Even if there are no syntactic changes to a line of code, a diff is created with the introduction of whitespace and asks someone reviewing the code to answer why something might have changed.<\/p>\n<p><code>diff --git a\/eyeglass.py b\/eyeglass.py<\/code><br \/>\n<code>index f8c515a..86387b3 100644<\/code><br \/>\n<code>--- a\/eyeglass.py<\/code><br \/>\n<code>+++ b\/eyeglass.py<\/code><br \/>\n<code>@@ -109,7 +109,7 @@ class Eyeglass:<\/code><\/p>\n<p><code>self.__endian__(bigendian)<\/code><\/p>\n<p><code>- with open(filename + \".eygl\", \"wb\") as file: &lt;-- original line<\/code><br \/>\n<code>+ with open(filename + \".eygl\", \"wb\") as file: &lt;-- added whitespace<\/code><br \/>\n<code>file.write(struct.pack(\"14s\", self.magic))<\/code><br \/>\n<code>file.write(struct.pack(self.byte, self.version)) # unsigned char<\/code><br \/>\n<code>file.write(struct.pack(self.bool, self.bigendian)) # bool<\/code><\/p>\n<h3>Markdown<\/h3>\n<p>Giving the increasing prevalence of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Markdown\" target=\"_blank\" rel=\"noopener\">markdown<\/a>, improving markdown&#8217;s consistency across projects is as important as code consistency. Markdownlint looks for issues with semantic headings, overly long lines, and other issues that might prevent correct display across platforms, as well as identifying improvements, such as marking up code snippets to make use of syntax-highlighting, thus improving readability.<\/p>\n<h2>Finding understanding<\/h2>\n<p>It is the automatic identification of coding issues that perhaps helps me the most when finding my way into a codebase.<\/p>\n<p>Linting tools will return issues with code, and the process of visiting those issues one-by-one to fix them, even if it isn&#8217;t your own codebase (it can always result in a pull-request!), helps one to grok the codebase and its intentions.<\/p>\n<p>I created a few example linting outputs below. Some are actual errors, which you are less likely to fund in an already working piece of code, but others are actual corrections you might want to start making to start to make it more readable and understandable.<\/p>\n<p><code><strong>example.py:1:0:<\/strong> C0114: Missing module docstring (missing-module-docstring)<br \/>\n<strong>example.py:1:0:<\/strong> C0116: Missing function or method docstring (missing-function-docstring)<br \/>\n<strong>example.py:1:17:<\/strong> C0103: Argument name \"x\" doesn't conform to snake_case naming style (invalid-name)<br \/>\n<strong>example.py:1:0:<\/strong> W0102: Dangerous default value [] as argument (dangerous-default-value)<br \/>\n<strong>example.py:5:4:<\/strong> R1705: Unnecessary \"else\" after \"return\", remove the \"else\" and de-indent the code inside it (no-else-return)<br \/>\n<strong>example.py:14:4:<\/strong> C0103: Variable name \"testVar\" doesn't conform to snake_case naming style (invalid-name)<br \/>\n<strong>example.py:18:7:<\/strong> C1802: Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty (use-implicit-booleaness-not-len)<br \/>\n<strong>example.py:11:8:<\/strong> W0612: Unused variable 'key' (unused-variable)<br \/>\n<strong>example.py:22:0:<\/strong> C0116: Missing function or method docstring (missing-function-docstring)<br \/>\n<strong>example.py:24:4:<\/strong> E0602: Undefined variable 'sys' (undefined-variable)<br \/>\n<\/code><\/p>\n<p>From the errors above:<\/p>\n<p>Adding docstrings (documentation strings), e.g. for the module and functions, asks that we look at a piece of code and answer what the code might be doing overall; and asks what its smaller units are trying to achieve. A docstring for the function <code>do_something()<\/code> might look as follows:<\/p>\n<div>\n<div>\n<pre>def do_something() -&gt; str:\r\n\u00a0 \u00a0\"\"\"This function performs a very specific function and outputs some\r\n\u00a0 \u00a0important information.\r\n\u00a0 \u00a0\"\"\"\u00a0 \u00a0\r\n\r\n<\/pre>\n<\/div>\n<\/div>\n<p>Flow of execution can be improved by looking at redundant constructs such as the &#8220;unnecessary else&#8221; above. Redundancy adds complexity and makes code more difficult to read and understand. Removing redundancy can help you to understand more precisely what something may be doing. More advanced concepts such as <a href=\"https:\/\/medium.com\/@matryer\/line-of-sight-in-code-186dd7cdea88\" target=\"_blank\" rel=\"noopener\">following the happy path<\/a> may follow after you start to pick up on these things.<\/p>\n<p>Unused variables are also redundant and once removed it becomes easier to see how the code fits together.<\/p>\n<p>Naming variables with meaningful names means that a reader can come along and immediately see what information a variable is supposed to hold. In only a small number of instances should they be single character names, e.g. <code>i<\/code> is often used for &#8220;index&#8221; but <code>idx<\/code> is often already a lot better; <code>x<\/code> could have any meaning and use. Identifying the use of <code>x<\/code> in my example code and trying to rename it helps to reveal the code&#8217;s intentions.<\/p>\n<p>There are some clues that some of this code may be dangerous. Having a look at why a list is supplied to a function as an argument (in this example) and considering how the code can be refactored to avoid this helps you to improve your understanding, as well as improve the code for the benefit of others. It may be that the function has too many responsibilities, or the function sits within a bigger process that can be further separated out into other smaller functions. There may also be other design patterns that the code can adopt that the original developer hasn&#8217;t considered.<\/p>\n<h2>In conclusion<\/h2>\n<p>While you might not want to see linting errors in a project, their existence in a codebase that you are trying to learn can be a benefit. You can start to walk through each of the issues in turn, cleaning up the codebase as you go, and at the end of the process you should be able to read things more clearly as well as understand what the code is doing.<\/p>\n<p>For your future projects, your use of linting will help make your work more understandable to someone else. As you develop your knowledge of a programming language, following the linting trail helps you to build your own knowledge base of good and bad practices and you will see the shape of your code improve as well as its readability and reliability.<\/p>\n<h3>Other benefits<\/h3>\n<p>There are other benefits to good linting, including improving the code review process. The first step of which is to have agreed standards, and making those machine actionable in the first instance means that colleagues can focus on more substantial changes that improve the quality of the project.<\/p>\n<p>Maintenance is a word I am yet to touch upon in this blog, but following an idiomatic approach to coding that improves its overall quality when it is first written helps improve its maintainability for others (and yourself when you revisit your past-self&#8217;s work). This is always something we want to consider in the field of digital preservation and beyond.<\/p>\n<h3>Like a compiled language<\/h3>\n<p>The output of linting such as in this template repository makes Python work much more like a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Compiled_language\" target=\"_blank\" rel=\"noopener\">compiled language<\/a> in which syntactical and semantic errors need to be identified up front so that code can be compiled into an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Executable\" target=\"_blank\" rel=\"noopener\">executable<\/a>. There are benefits and drawbacks as one might expect. Sometimes the number of issues are very high the first time the tools are run. It may take a while to fix a large number. On the other hand, as you get more used to the errors, you also become more articulate in the language, creating fewer and fewer issues as your write your code.<\/p>\n<h3>Give it a try!<\/h3>\n<p>This is probably one of the hardest blogs I&#8217;ve tried to write &#8211; making very explicit (probably overly so) something that works very seamlessly when put into practice. Once linting tools are added to a Python project it is as simple as running <code>python -m tox -e linting<\/code>, and then following the output.<\/p>\n<p>The <a href=\"https:\/\/github.com\/pypa\/sampleproject\" target=\"_blank\" rel=\"noopener\">sample project<\/a> is available for anyone to use.<\/p>\n<p>If you give it a whirl, let me know! If there are improvements you&#8217;d like to see made, either leave an issue on GitHub, or let me know in the comments here. Alternatively, submit a pull request!<\/p>\n<p>Finally, let me know what some of your favorite linting tools are and any that I should be using!<\/p>\n<h3>More information<\/h3>\n<ul>\n<li>As I was writing this blog a potentially useful study group for Python was announced that may interest readers: <a href=\"https:\/\/www.dpconline.org\/events\/eventdetail\/211\/-\/python-study-group-launch-and-information-session\" target=\"_blank\" rel=\"noopener\">https:\/\/www.dpconline.org\/events\/eventdetail\/211\/-\/python-study-group-launch-and-information-session<\/a><\/li>\n<li>I write a little bit more about the benefits of learning to code in the GLAM sector here: <a href=\"https:\/\/exponentialdecay.co.uk\/blog\/context-switching-do-you-really-need-to-be-an-archivist-programmer-one-perspective\/\" target=\"_blank\" rel=\"noopener\">https:\/\/exponentialdecay.co.uk\/blog\/context-switching-do-you-really-need-to-be-an-archivist-programmer-one-perspective\/<\/a><\/li>\n<\/ul>\n<h3>A point of note<\/h3>\n<p>I didn&#8217;t go into the fact that some of the linting messages also have &#8220;codes&#8221; associated with them, e.g.<\/p>\n<ul>\n<li><code>'example.py:22:0: <span style=\"text-decoration: underline;\">C0116<\/span>: Missing function or method docstring (missing-function-docstring)' <\/code><\/li>\n<\/ul>\n<p>the code for this message here is <code>C0116<\/code>.<\/p>\n<p>These codes can be traced back to different industry standards for what they mean, and pylint which I find one of the more useful tools for its messages, lists these here: <a href=\"https:\/\/pylint.readthedocs.io\/en\/latest\/user_guide\/messages\/messages_overview.html\" target=\"_blank\" rel=\"noopener\">https:\/\/pylint.readthedocs.io\/en\/latest\/user_guide\/messages\/messages_overview.html<\/a>. Marking up error messages like this is something we&#8217;ve been trying to do in digital preservation as well, such as in <a href=\"https:\/\/github.com\/openpreserve\/jhove\/wiki\" target=\"_blank\" rel=\"noopener\">JHOVE<\/a>. Maybe there&#8217;s some more we can learn from this linting example than I have already described?<\/p>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_1423\" class=\"pvc_stats total_only  \" data-element-id=\"1423\" style=\"\"><i class=\"pvc-stats-icon small\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/exponentialdecay.co.uk\/blog\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n","protected":false},"excerpt":{"rendered":"<p>I have been working on a Python template repository as part of my day-job at <a href=\"https:\/\/orcfax.io\/\" target=\"_blank\" rel=\"noopener\">Orcfax<\/a>.<\/p>\n<p>It is based on the popular pypa <a href=\"https:\/\/github.com\/pypa\/sampleproject\" target=\"_blank\" rel=\"noopener\">sample project<\/a> and adds important tooling that supports the quality assurance of projects that many developers are expected to engage with.<\/p>\n<p>In my template repository I add editor defaults, linting, and prepare the repository for unit tests, and then deployment.<\/p>\n<p>I have migrated a copy of the template I created for Orcfax to a new file format organisation I have created to capture work I am doing around tools such as <a href=\"https:\/\/ffdev.info\" target=\"_blank\" rel=\"noopener\">ffdev.info<\/a> (the PRONOM signature development utility).<\/p>\n<p>The new template repository can be found here: <a href=\"https:\/\/github.com\/ffdev-info\/template.py\" target=\"_blank\" rel=\"noopener\">ffdev-info\/template.py<\/a>.<\/p>\n<p>I want to talk about how this tooling can be used as a way of understanding legacy, or new code that you are going to be looking at. Looking at how linting can be useful for learning and understanding.<\/p>\n<div class=\"link-more\"><a href=\"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &ldquo;Linting as understanding&rdquo;<\/span>&hellip;<\/a><\/div>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_1423\" class=\"pvc_stats total_only  \" data-element-id=\"1423\" style=\"\"><i class=\"pvc-stats-icon small\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/exponentialdecay.co.uk\/blog\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n","protected":false},"author":1,"featured_media":1428,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":3,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"federated","footnotes":""},"categories":[114,3,20,19],"tags":[61,96,198,173,42,41,97],"class_list":["post-1423","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-digital-literacy","category-digital-preservation","category-just-code","category-python","tag-code","tag-coding","tag-linting","tag-maintenance","tag-open-source","tag-oss","tag-software-development","entry"],"a3_pvc":{"activated":true,"total_views":350,"today_views":0},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Linting as understanding - ross spencer :: exponentialdecay.digipres :: blog<\/title>\n<meta name=\"description\" content=\"An overview of how linting can serve as an initial step in the process of learning and analyzing both new and legacy codebases.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Linting as understanding - ross spencer :: exponentialdecay.digipres :: blog\" \/>\n<meta property=\"og:description\" content=\"An overview of how linting can serve as an initial step in the process of learning and analyzing both new and legacy codebases.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/\" \/>\n<meta property=\"og:site_name\" content=\"ross spencer :: exponentialdecay.digipres :: blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-10-10T12:33:49+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-26T19:53:36+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/exponentialdecay.co.uk\/blog\/wp-content\/uploads\/2023\/10\/houston_safety-scaled.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1918\" \/>\n\t<meta property=\"og:image:height\" content=\"2560\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Ross Spencer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@beet_keeper\" \/>\n<meta name=\"twitter:site\" content=\"@beet_keeper\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ross Spencer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/\"},\"author\":{\"name\":\"Ross Spencer\",\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/#\\\/schema\\\/person\\\/4cae0a954400f42b9c1b70c699837716\"},\"headline\":\"Linting as understanding\",\"datePublished\":\"2023-10-10T12:33:49+00:00\",\"dateModified\":\"2025-06-26T19:53:36+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/\"},\"wordCount\":2071,\"commentCount\":3,\"publisher\":{\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/#\\\/schema\\\/person\\\/4cae0a954400f42b9c1b70c699837716\"},\"image\":{\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/houston_safety-scaled.jpg\",\"keywords\":[\"Code\",\"Coding\",\"Linting\",\"maintenance\",\"Open Source\",\"OSS\",\"Software Development\"],\"articleSection\":[\"Digital Literacy\",\"Digital Preservation\",\"Just Code\",\"Python\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/\",\"url\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/\",\"name\":\"Linting as understanding - ross spencer :: exponentialdecay.digipres :: blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/houston_safety-scaled.jpg\",\"datePublished\":\"2023-10-10T12:33:49+00:00\",\"dateModified\":\"2025-06-26T19:53:36+00:00\",\"description\":\"An overview of how linting can serve as an initial step in the process of learning and analyzing both new and legacy codebases.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/#primaryimage\",\"url\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/houston_safety-scaled.jpg\",\"contentUrl\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/houston_safety-scaled.jpg\",\"width\":1918,\"height\":2560,\"caption\":\"Stop, Look, Listen, retro game style advertising for safety at a Houston Bus Stop\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/linting-as-understanding\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Linting as understanding\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/\",\"name\":\"ross spencer :: exponentialdecay.digipres :: blog\",\"description\":\"Digital preservation analyst, researcher, and software developer\",\"publisher\":{\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/#\\\/schema\\\/person\\\/4cae0a954400f42b9c1b70c699837716\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/#\\\/schema\\\/person\\\/4cae0a954400f42b9c1b70c699837716\",\"name\":\"Ross Spencer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/avatar-scaled.png\",\"url\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/avatar-scaled.png\",\"contentUrl\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/avatar-scaled.png\",\"width\":2560,\"height\":2560,\"caption\":\"Ross Spencer\"},\"logo\":{\"@id\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/avatar-scaled.png\"},\"description\":\"Digital preservation domain expert and full-stack software developer.\",\"sameAs\":[\"http:\\\/\\\/www.exponentialdecay.co.uk\\\/blog\",\"https:\\\/\\\/www.instagram.com\\\/b33tk33p3r\\\/\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/ross-spencer-b6b9b758\\\/\",\"https:\\\/\\\/x.com\\\/beet_keeper\"],\"url\":\"https:\\\/\\\/exponentialdecay.co.uk\\\/blog\\\/author\\\/exponentialdecay\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Linting as understanding - ross spencer :: exponentialdecay.digipres :: blog","description":"An overview of how linting can serve as an initial step in the process of learning and analyzing both new and legacy codebases.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/","og_locale":"en_US","og_type":"article","og_title":"Linting as understanding - ross spencer :: exponentialdecay.digipres :: blog","og_description":"An overview of how linting can serve as an initial step in the process of learning and analyzing both new and legacy codebases.","og_url":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/","og_site_name":"ross spencer :: exponentialdecay.digipres :: blog","article_published_time":"2023-10-10T12:33:49+00:00","article_modified_time":"2025-06-26T19:53:36+00:00","og_image":[{"width":1918,"height":2560,"url":"https:\/\/exponentialdecay.co.uk\/blog\/wp-content\/uploads\/2023\/10\/houston_safety-scaled.jpg","type":"image\/jpeg"}],"author":"Ross Spencer","twitter_card":"summary_large_image","twitter_creator":"@beet_keeper","twitter_site":"@beet_keeper","twitter_misc":{"Written by":"Ross Spencer","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/#article","isPartOf":{"@id":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/"},"author":{"name":"Ross Spencer","@id":"https:\/\/exponentialdecay.co.uk\/blog\/#\/schema\/person\/4cae0a954400f42b9c1b70c699837716"},"headline":"Linting as understanding","datePublished":"2023-10-10T12:33:49+00:00","dateModified":"2025-06-26T19:53:36+00:00","mainEntityOfPage":{"@id":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/"},"wordCount":2071,"commentCount":3,"publisher":{"@id":"https:\/\/exponentialdecay.co.uk\/blog\/#\/schema\/person\/4cae0a954400f42b9c1b70c699837716"},"image":{"@id":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/#primaryimage"},"thumbnailUrl":"https:\/\/exponentialdecay.co.uk\/blog\/wp-content\/uploads\/2023\/10\/houston_safety-scaled.jpg","keywords":["Code","Coding","Linting","maintenance","Open Source","OSS","Software Development"],"articleSection":["Digital Literacy","Digital Preservation","Just Code","Python"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/","url":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/","name":"Linting as understanding - ross spencer :: exponentialdecay.digipres :: blog","isPartOf":{"@id":"https:\/\/exponentialdecay.co.uk\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/#primaryimage"},"image":{"@id":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/#primaryimage"},"thumbnailUrl":"https:\/\/exponentialdecay.co.uk\/blog\/wp-content\/uploads\/2023\/10\/houston_safety-scaled.jpg","datePublished":"2023-10-10T12:33:49+00:00","dateModified":"2025-06-26T19:53:36+00:00","description":"An overview of how linting can serve as an initial step in the process of learning and analyzing both new and legacy codebases.","breadcrumb":{"@id":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/#primaryimage","url":"https:\/\/exponentialdecay.co.uk\/blog\/wp-content\/uploads\/2023\/10\/houston_safety-scaled.jpg","contentUrl":"https:\/\/exponentialdecay.co.uk\/blog\/wp-content\/uploads\/2023\/10\/houston_safety-scaled.jpg","width":1918,"height":2560,"caption":"Stop, Look, Listen, retro game style advertising for safety at a Houston Bus Stop"},{"@type":"BreadcrumbList","@id":"https:\/\/exponentialdecay.co.uk\/blog\/linting-as-understanding\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/exponentialdecay.co.uk\/blog\/"},{"@type":"ListItem","position":2,"name":"Linting as understanding"}]},{"@type":"WebSite","@id":"https:\/\/exponentialdecay.co.uk\/blog\/#website","url":"https:\/\/exponentialdecay.co.uk\/blog\/","name":"ross spencer :: exponentialdecay.digipres :: blog","description":"Digital preservation analyst, researcher, and software developer","publisher":{"@id":"https:\/\/exponentialdecay.co.uk\/blog\/#\/schema\/person\/4cae0a954400f42b9c1b70c699837716"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/exponentialdecay.co.uk\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/exponentialdecay.co.uk\/blog\/#\/schema\/person\/4cae0a954400f42b9c1b70c699837716","name":"Ross Spencer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/exponentialdecay.co.uk\/blog\/wp-content\/uploads\/2025\/06\/avatar-scaled.png","url":"https:\/\/exponentialdecay.co.uk\/blog\/wp-content\/uploads\/2025\/06\/avatar-scaled.png","contentUrl":"https:\/\/exponentialdecay.co.uk\/blog\/wp-content\/uploads\/2025\/06\/avatar-scaled.png","width":2560,"height":2560,"caption":"Ross Spencer"},"logo":{"@id":"https:\/\/exponentialdecay.co.uk\/blog\/wp-content\/uploads\/2025\/06\/avatar-scaled.png"},"description":"Digital preservation domain expert and full-stack software developer.","sameAs":["http:\/\/www.exponentialdecay.co.uk\/blog","https:\/\/www.instagram.com\/b33tk33p3r\/","https:\/\/www.linkedin.com\/in\/ross-spencer-b6b9b758\/","https:\/\/x.com\/beet_keeper"],"url":"https:\/\/exponentialdecay.co.uk\/blog\/author\/exponentialdecay\/"}]}},"views":2244,"_links":{"self":[{"href":"https:\/\/exponentialdecay.co.uk\/blog\/wp-json\/wp\/v2\/posts\/1423","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/exponentialdecay.co.uk\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/exponentialdecay.co.uk\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/exponentialdecay.co.uk\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/exponentialdecay.co.uk\/blog\/wp-json\/wp\/v2\/comments?post=1423"}],"version-history":[{"count":43,"href":"https:\/\/exponentialdecay.co.uk\/blog\/wp-json\/wp\/v2\/posts\/1423\/revisions"}],"predecessor-version":[{"id":2559,"href":"https:\/\/exponentialdecay.co.uk\/blog\/wp-json\/wp\/v2\/posts\/1423\/revisions\/2559"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/exponentialdecay.co.uk\/blog\/wp-json\/wp\/v2\/media\/1428"}],"wp:attachment":[{"href":"https:\/\/exponentialdecay.co.uk\/blog\/wp-json\/wp\/v2\/media?parent=1423"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/exponentialdecay.co.uk\/blog\/wp-json\/wp\/v2\/categories?post=1423"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/exponentialdecay.co.uk\/blog\/wp-json\/wp\/v2\/tags?post=1423"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}