The Surprising Extent of Non-Code Dependencies in Software Projects

Software isn't just dependent on formal entities like libraries, modules, and APIs–it also depends on time, the law, pre-existing documentation, local custom, and even individual people

This article is part of my Confessions of an Unintentional CTO book, which is currently available to read for free online.

Programming blogs frequently muse about software dependencies, and there are reams of articles devoted to questions such as: How do we keep the number of dependencies down? How do we choose reliable third-party libraries? How do we manage large groups of them, given that they are prone to squabble and conflict with one another? How do we architect code that stays maximally intact when one of these dependencies changes? How do we programmatically test systems with many dependencies?

All these discussions are worthwhile. But today I want to focus on the kinds of dependencies that go beyond the usual suspects, i.e. dependencies that don’t conform to the familiar shape of module, library, or software packages—dependencies that aren’t even code.

What might a non-code dependency even look like? Well imagine a programmer—e.g. me, the author—in charge of software that sells study materials to help students. Due to having been in this business for a while, I’ve gained an increasingly advanced understanding of my customers and their problem domain. In particular, I eventually learned that there was absolutely no demand for study products like audio packages or flash card decks. Instead of study products, my customers only wanted study notes. This late realisation made it clear that I should modify the (existing) product page URLs from “/products/bla-bla-bla” to “/notes/bla-bla-bla”. This change would not only improve SEO for my target market but also help harmonise the vocabulary I employ across my business.

You might think that a URL revision like this would be a laughably simple change, requiring only a few tiny edits in the router file, the relevant controller files, and the corresponding view templates. The integration tests would need a little tweaking too, but then the code ought to be good to go, and I should be able to get back home in time for dinner.

Right?

While it’s true that my code changes could be as simple as described above, this rosy picture is complicated by the presence of non-code dependencies, which arise from the fact that I’m running a real-world business complete with customers, staff, API integrations, documentation, legal promises, and suppliers. I’ve got baggage, and this baggage exists regardless of whether I happen to realise I’m carrying it at the time. Underestimating its extent is reckless, for it leaves me unprepared for forthcoming problems—problems that would be exacerbated by my ignorance.

Without further ado, let me introduce you to the various non-code dependencies I encountered, all of which required corresponding changes:

1. Project Workers and Stakeholders

Once the URL was changed, I had to inform my administrative and customer service staff about the relocation. Their ability to operate my business depends on them knowing which URLs lead to which resources, so I need to keep them abreast of my changes.

2. Business Process Documentation (and Programmer READMEs)

I subscribe strongly to the idea espoused within the business book The E-Myth Revisited that a business ought to have up-to-date operating manuals describing how best to fulfill common business processes. After my URL change, I saw that some instructions in my business’ manuals included references to my old URL and thus had to be updated.

We need to be vigilant in keeping our operating manuals up to date–otherwise future readers would not only become confused but would also lose general trust in our documentation process, eventually coming to view our “process”-driven approach with suspicion.

README docs intended for the programming team didn’t need updating after this particular change, but generally speaking, you’ll want to keep your eyes on theses docs since they usually constitute an important source of non-code dependencies. Nothing pisses off an engineer more than defunct documentation.

3. Third-Party Web Services and Digital Suppliers

I’ll explain this point with a series of examples:

  • I use Google and Facebook online advertising to send traffic to my product URLs. Following the change to my URL structure, these adverts 404ed, causing a plummet in revenue. Of course, I should have used a redirect to avoid the 404, but even this approach would’ve had the disadvantage of slowing down the request time for all my expensively bought potential customers. The best solution would be to update the URLs within the advertisers’ platforms.
  • Google Analytics broke in all sorts of ways too: My sales funnels and conversion tracking goals stopped recording data as intended because they both expected URLs starting with “/products/”—and these URLs no longer existed. Additional confusion cropped up when my assistant was in Google Analytics exploring data that spanned the time period before and after the URL transition. His figures seemed WAY off at first, and we only arrived at the right numbers when we realised we needed to input both the new and the old product URL structures, otherwise we would have excluded half the relevant data. It’s a miracle we even remembered this oddity; we very nearly decided to stop advertising seriously profitable products based on the initial disappointing figures we saw before adding the old URL into our query. I shudder to think about how easily “data-driven” decisions can get poisoned through forgotten snags.

One’s software can be dependent on third-party platforms in all sorts of other scary ways, which I’ll sloppily sum up as platform risk. This didn’t affect me in this particular case with my URLs, but I’d like to bring up platform risk anyway since it’s important. As many avid Hacker News readers likely know, policy changes on Facebook or newly added rate limiters on Twitter can devastate thriving businesses. Likewise, the bankruptcy and closure of an important SAAS provider can knock out slabs of your functionality and cripple your ability to serve your own customers. Choose your software partners very very carefully.

4. Law and Regulations

Given that I read law at university, it’s no surprise that I’m going to bring up the increasing number of dependencies that exist between your website and the law. Changes to laws can necessitate changes to your code. Two recent (and infuriating) examples of such law changes are (1) the harebrained EU cookie directive (the only purpose of which seems to be to bolster the incidence of computer-related RSI), and (2) the “Anti-Amazon Law”, which rewrote European sales tax law such that suppliers of digital services (and even ebooks) must calculate, levy and pay sales tax to whichever country the customer is based in—with no threshold below which you don’t have to file. I once had a sales tax bill of €4.41 in Poland and accidentally transferred their government €4.40. A few months later I received a letter in Polish—saying that I owed their government €0.01. No wonder Britain left the EU…

Conversely, code changes can sometimes require legal (or more specifically, contractual) changes. If I about-face the way I calculate commissions for the authors selling documents on my website, my contractual pages ought to reflect this new way of doing things, and I darned well better alert all the authors affected. Likewise, if I install a new type of tracking technology (conversion pixel, remarking tag, etc.), my privacy policy ought to explain what I’m doing and how I intend to use the newly acquired data. Apparently it is not OK to honestly level with your customers and explain that “we don’t give a damn about your personal life/details and the only reason we deploy every tracking technology, trick, and technique known to man is to calculate how to make as much money as humanly possible from you”.

5. Language, Geography, and Local Custom

As anyone who has done internationalisation work will know, dependencies exist between your codebase and your localisation and translation files. When I changed my URLs on my UK site to read “notes” instead of “products”, this necessitated corresponding changes in other locales I operate in. For example, American search engines rank the word “outlines” higher than the word “notes”, so I had to modify my URLs (and Analytics, and on-page text, etc.) to say “outlines” across the pond.

6. Time

If you need any proof that dependencies exist between websites and time, I invite you to think about how many website footers you’ve seen in the last week that read “Rights Reserved 2013” even though years have since passed. These authors were blind to the dependency between code and time.

More insidiously, dependencies between code and time can wreak havoc on temporal database search queries and on their automated tests. I won’t explore this topic today, save to say “watch out”.

7. Design and Fonts

Dependencies exist between code and your design. The word “notes” has a few characters less than the word “products”, and thanks to my shoddy CSS skills, this three-character difference was enough to spoil the alignment between my homepage’s header element (“Buy These Notes”) and the paragraph immediately underneath, granting my website a distinctly charlatan charm.

Although fonts didn’t prove to be an issue with my URL change, I want to point out that programs/files that demand the availability of specific fonts (e.g. Garamond) may end up garbled when viewed on computers that don’t have those fonts installed. I ran into this problem when writing my first book with LaTeX. My big takeaway was this: Always package your fonts along with your project. Just because a font is available on your computer doesn’t mean it will be available on anyone else’s.

8. Data and State (i.e. Dependencies between Code and Data)

This particular type of non-code dependency didn’t afflict me with respect to my URL renaming saga, so I’m going to illustrate my point with a different example: Say that a year after launching my web application, I add a new database attribute to my Product table—let’s say that I add the “exam_year” field. In order to carry out this update correctly, I ought to remember that all my Product table entries before this point were entered without exam years. I face a dilemma:

  • Do I add “exam_year” data retrospectively to all existing products, obtaining the data by asking the original authors one by one? Is there even the remotest of chances that they’d bother answering these queries?
  • Should I change the columns of all the historical records to a default, such as “unknown”?
  • Do I allow the column to remain as NULL in the database and promise myself never to make “exam_year” required in any part of the system (e.g. within my reporting code or my consumer-facing views)?

If I failed to realise this coupling between database state and code, I could eventually be saddled with painful bugs—bugs that might only show themselves long after I had made this exam_year change. (Explanation: I believe recency of change-causing-failure is a potent bug-fighting tool.)

Another frequently occurring specimen in this dependency category is when code expects certain folder hierarchies to be present on a computer. Folders, after all, are a kind of OS state.

Dealing with These Dependencies

Unfortunately there’s not a whole lot to recommend here, other than a few conscientious practices:

Be aware and mindful of these dependencies. Run through them in checklist form after a major change and strive to design your business in a way that minimises all dependencies, and not just code ones.

Record specific dependencies within your code comments, tests, and business-process documents. You want to be reminded to consider them at the right time in future.

Write Git commit hooks that print out helpful reminders to consider certain dependencies. For example, your Git hook might look for modifications to the routes file and then print out a recommendation to update online advertising and analytics configurations.


More Articles:

Dworkinian Integrity and (Sub-)Symbol Minimisation: Prescriptions for Consistent Software

How the perfect unbuilt API already exists, floating in the zeitgeist, and how it is up you to pay attention to the totality in front of you


Web Developers: Harmonise Your Time Zones

How it's all too easy to wind up with divergent time zones across a modern web stack


Project-Level Attribute Normalisation

Why you should introduce a software layer close to your object relational mapper that formats attributes into clean, standardised forms