Posts

Terminology of CICD

To be clear, there are two types of software: libraries and applications. Libraries are used by applications to provide some key piece of functionality. Applications are always deployed to some Environment and run to perform computation. It should always be clear which one of the types is being implemented in a given piece of code. Building and publishing a new version of source code which is ready for use is called Releasing the library/application. The release is always associated with a new version number. Act of change to use a new version of an application in a given environment is called a Deployment. Deployments may also be other system or data changes that affect state produced by the application or general behavior of the application. All such changes should be associated with some code changes making them easy to track.

On Testing

Test a piece runs A basic building block of a codebase is a function. So for each building block let's just at the very least test it can be called with some reasonable input and produce the right result. If you feel that’s not needed perhaps you don’t need that function at all. That is a base case for testing that prevents breaking what works. It should be enabled to run on all PRs. Every test case should have a human understandable description of the test (LLMs will be able to generate those tests soon, so better be ready). Test it runs end to end Once a bunch of functions are composed things get tricky to check if it all works. So let's test if a thing can be run end to end and produce something sensible at the end. It can be enabled on all PRs and definitely done before any release. Test pieces produce similar results Once a thing runs end to end it will produce a result. But it's hard to say that the result is right without anything to compare. So instead, we will test...

Perils of classes and pointers

When trying to understand or debug a piece of code the amount of state that one needs to keep in mind is inversely proportional with success. This leads us to an obvious advice to try to limit scope of each function to the essential components. However, there are also certain programming concepts that contribute to the amount of state one needs to track. The first one is the use of classes because class attributes are accessible in each method and may change between calls. The second one is a pointer (or reference). Pointers are problematic because, unless explicitly managed like in rust , they make it difficult to determine the owner of data at a given point of computation. This leads to uncertainty about what state a given variable may hold. Be very careful when adding classes and references to your code. Sometimes a pure function is just better.

On engineering realism

A lot of engineering advice is normative in form: “you should do X and avoid Y, because Y is bad engineering”. The nagging character of such statements can create a feeling that Y shouldn’t exist at all. After all it’s bad engineering so why should it be tolerated? There are of course explanations that point to user or business needs being more important than engineering aspects. To add to that, our feelings towards bad engineering should be of a different form. Rather than deny their existence we should acknowledge that bad engineering simply exists and our efforts should be focused on creating an environment where it can not flourish. So when we point out some bad piece of engineering we are simply stating the fact. We are not trying to accuse or attack anyone. Besides fixing the one piece which we believe is broken we should also ask: why did it happen? What can we change about our process to make it less possible to happen? In essence, we should be realists. We should feel that ...

On explainability

An important aspect of maintaining software is our ability to explain exactly how and why it solves a problem. Consider this way of introducing a developer to a new piece of software: “When you do Y, it’s a problem how to do X. This software attempts to help with doing X by doing Z. This works well because Z has the following advantages: a, b and c and some disadvantages: q, r, s. If those trade-offs are appropriate for your domain consider using this piece of software.” I consider this a Gold Standard for introduction to the point of a library or application. It explains why it exists, what it does and when to use it. Following such an introduction, we need to offer a deeper dive into the software with the goal of making one fully proficient in using it. I propose the following setup for documenting each project: Introduction: Gentle introduction to main concepts Getting started: Installation: setup steps to install software Testing your setup: how to test your setup works as expecte...

On CLIs

90% of application domain development is deciding what are nouns and verbs of the solution (do not believe this number). Nouns are the state applications will ingest and produce. Verbs are actions the app will take to achieve this. Designing a CLI forces us to answer those questions. We can decide what language the application domain actually speaks. This is an invaluable property when explaining how an application works to your colleagues. It helps to improve application maintainability by providing a simple interface to use. It also prepares us for designing an API around the application if it's ever needed. There are repeatable patterns in design of CLI applications that are not about solving problems in the application domain. We can call those aspects “infrastructure of CLI applications”. Common elements are: Layout of application source files and various coding conventions. Managing state with Resources: CRUD operations can be applied to Resources and the application specifie...

On Simplicity

We now know what we need in place to build software products. We can now ask a question: how should we build it? If there’s one rule I can confidently vouch for is this one: do it simply. Simple is not easy though. Simple is in fact very hard. But it is very worth it to enforce it. Recall the trouble of building a product: requirements change, new features need to be added, bugs need to be fixed, existing features need to be improved. In this chaos there needs to be some sanity. One principle I come back to again and again is I want the software to be simple. I want it to be dead boring and predictable. I groan when I see complicated code. Code is not a product, it is a liability. Do what is needed. Best part is no part. Do one thing at a time. Doing it in a shorter way is usually better than a longer way. Do it in a way that you can explain easily. Do it with fewer pieces. Do it by not sharing state. Do it in stages. That list goes on and contains lots of facets based on one’s exper...