
Cross-compiling C++ with Bazel and Wolfi
rules_apko, toolchains_llvm and Wolfi

Pre-Building Standard Devcontainers with GitHub CI
To support Left-of-Launch quality checks and ensure a consistent development environment straight out of GitHub, I make extensive use of VS Code’s devcontainers. The one problem is once you have built up a large set of tools, the time to rebuild the container gets to be quite long -- unnecessarily so, because the vast majority of the layers in the container never change. Ideally we want the majority of our core features to just be available as a pre-built image. If you search for how to do th...

Chaos Manor Reloaded
Many years ago Jerry Pournelle wrote a column for the now-defunct Byte magazine called Computing at Chaos Manor. Nowadays a lot of system administrators and developers are homelabbing and writing about it, but he was probably one of the pioneers, so the title here is a nod to that history. As I am getting back to regular hobbyist software development and being our home’s Bastard Operator From Hell, I wanted to write more about that as well. In the past I have written while building both hardw...

Cross-compiling C++ with Bazel and Wolfi
rules_apko, toolchains_llvm and Wolfi

Pre-Building Standard Devcontainers with GitHub CI
To support Left-of-Launch quality checks and ensure a consistent development environment straight out of GitHub, I make extensive use of VS Code’s devcontainers. The one problem is once you have built up a large set of tools, the time to rebuild the container gets to be quite long -- unnecessarily so, because the vast majority of the layers in the container never change. Ideally we want the majority of our core features to just be available as a pre-built image. If you search for how to do th...

Chaos Manor Reloaded
Many years ago Jerry Pournelle wrote a column for the now-defunct Byte magazine called Computing at Chaos Manor. Nowadays a lot of system administrators and developers are homelabbing and writing about it, but he was probably one of the pioneers, so the title here is a nod to that history. As I am getting back to regular hobbyist software development and being our home’s Bastard Operator From Hell, I wanted to write more about that as well. In the past I have written while building both hardw...
Share Dialog
Share Dialog

Subscribe to Kyle Downey

Subscribe to Kyle Downey
<100 subscribers
<100 subscribers


The concept “left of launch” comes from missile defense: in the timeline, the events that happen before the missile launch are to the left; this is where the idea of Shift Left in QA testing comes from as well. You can apply this idea to building code, unit testing, benchmarking and even security scanning: the common thread is that in all these things when there is a mistake of some kind, you want it to become apparent as soon after the mistake is made as possible. Why you might want that, and why it’s so important for a certain class of problems, is what we’ll be covering in this blog post.
I am devoting a short blog post to this idea because it’s going to come up again and again in this series, and rather than repeating myself I am just going to link back here. It’s also because this is about programming philosophy and programmer psychology more than technology or techniques, and it’s going to be a different kind of post than most -- and may not be everyone’s cup of tea.
This essay’s ideas do not apply to every programming project, and in fact there are some projects for which this is a terrible approach. It specifically applies to systems delivering value where unexpected failure has material consequences, so you can immediately rule out prototypes, spikes and student projects. It also requires that the blast radius for a failure is knowable at least approximately: for instance, that it’s OK to lose 30 cents, and not OK to lose $30K, or that you can tolerate one out of a million clients retrying a connection due to an error but not 50 of them.
Put more simply, we are in a classic engineering problem domain here. We cannot talk about whether something is built well or over-engineered without knowing what are the engineering tolerances; it’s not a sensible conversation. And of course, engineered things are built for a purpose and there are consequences when they fail.
In a software engineering project that respects the Left-of-Launch principle, you cannot deploy code containing a mistake whose consequences exceed the blast radius. In fact, you cannot make forward progress on any action if the consequences of that action go beyond the agreed threshold for that stage. Which means:
some code commits or pushes may be blocked
some code merges may be blocked
some code promotions may be blocked
Importantly, this is a calibrated escalation. The probability of blocking should be a function of the consequences of failing to block the change and the window of time afforded to recognize and repair the mistake, as well as the cost. Big consequences that afford you no time to respond thus have much stricter controls than small consequences that you can fix at your leisure. And wherever possible trivial issues should be addressed at point of discovery at zero cost, e.g. with a pre-commit auto-fix.
This section could also be titled ‘what bad looks like.’ If we push out code that we all know to have significant flaws and if propagation of those flaws have consequences, we are engaging in magical thinking:
we think no one will notice
we think it might not be so bad
we think the Magical Code Fairies will come by later and clean up the mess
The labor of the Magical Code Fairies includes fixing compiler warnings; removing TODO items that are not tracked in Jira; fixing known bugs that are not tracked in Jira; adding test coverage; and, for the truly damned spirits of that realm, data scrubbing.
They are mythical creatures.
The cost of defects escalates over time; this is well known. But I’d like to add to this that how you pay also matters. if the cost of quality control is amortized across the development process -- five minutes of additional work per pull request rather than an hour clean-up task -- it has a second-order productivity impact. For developers, long blocks of quiet time are essential for building the most complex components but due to coordination costs as teams increase in size, developers get fewer of these long blocks in a week. Do you really want to spend one of those open hours cleaning up warnings?
If you shift quality problem detection further left, it gets closer to the person who caused the problem. This increases accountability: you break it; you fix it, while at the same time ensuring the person who takes responsibility for the fix has the maximum context to help with a swift resolution. But in team psychology, there is an even greater win if you can ensure a culture of owning problems.
It’s not entirely true that the Magical Code Fairies are a myth. They do exist, but they have a tendency to burn out. You probably have worked with someone who took on all sorts of thankless tasks in a coding project … for a time. But this person probably got angry and frustrated for taking this on, perhaps at the expense of making more visible progress on other parts of the system, and moved on.
Resentment is the single most demotivating feeling. It should have no place in a high-performing team, which is why in Shift-Left we not only avoid blaming, but we also -- via accountability -- ensure that mistakes are fixed by people who made them, as they make them, not someone else later.
Ideally, this also motivates investments in ensuring that preventable things which are low-impact annoyances are resolved automatically rather than making it a person’s job. Better to use that capacity to add one EngProd developer so it is no one’s job!
Years ago I was talking to a developer on a team that used a monorepo for development of a complex trading system -- fairly rare at the time. They made a major release every Friday and 2 - 3 small patches a week, which in a bank environment was eye-opening for me. Everyone checking in code globally to one codebase, with high stakes, and they were delivering multiple times a week? I don’t think the term continuous deployment was even in use at the time, but that’s what they had. In his opinion, this was due to one and only one thing: their investment in automated testing meant they were not afraid to release, and slowing down to write tests let them move faster. This radical idea has become more common over time, but time and again I am told that investments in test automation, better builds, etc. is a tax on progress, when the opposite is the case: underinvestment in such things is the tax.
Again, there is a second-order value to getting this right. Teams that are less afraid of change of any kind are more willing to undertake the radical changes needed to get to the next level. We have all been there: I would do this refactoring, upgrade this library, swap out this server, but I don’t know what will happen. This fear also saps productivity, because it excuses build-up of technical debt.
One of the finest engineering managers I ever worked for had a rule about continuous improvement: he would leave a small portion of time for internal investment in every project. While the motivating benefits outlined here drove most of that investment, he pointed out another psychological piece: software development is neither art nor science, it’s craftsmanship -- the intersection of the two -- and pride in a piece of work is an essential motivating factor for individuals and teams. Delivery is a point of pride, but the things no one will ever see and that only a master builder will fully appreciate instill a deeper and more lasting pride than anything else.
If we achieve all these things from Shift-Left -- lower cost; accountability for mistakes; fearlessness about change; and pride in what we build -- we will be more productive and move faster. This is the essential argument being made in this essay, namely that investing in this approach and sometimes blocking progress makes you go faster. The trade-off between speed and quality is maybe not a complete myth, but it’s overdone.
As we are talking about engineering, it’s worth talking about the trade-offs of this approach, because it also involves trade-offs.
In the short run, eating the seed corn fills your stomach as well as the crop, and you can have it straight away. This is not an easy discipline for anyone on the team or for the broader organization. Pragmatism requires that we must violate these principles in a crisis, and in small, fast-moving organizations like a startup, crisis is always with us. There is always a good reason to ignore all of the above, and maybe your investors tell you that if you just make this delivery and get out to market the money will rain down and this can be used to hire as many Magical Code Fairies as you need to rewrite it. And here’s the worst part: they are not wrong; we need to live to fight another day as our first priority, before all of the above.
You will not win every argument on this one. The point of this essay is to arm you with the arguments for why you must win more of them over time to keep the organization healthy. It’s a struggle that requires persistence, and faith that it’s worth doing.
One developer’s trivial quality concern is deeply offensive to another; I remember one threat of quitting over the placement of braces in Java code, and we should bear in mind that creation is an emotional matter, and a large software codebase is like a 1000 page novel written by a dozen different authors, in parallel, and under pressure: the human mind under stress can fixate on trivial matters. Thus another hazard with shifting left is it can feed this tendency. The team thus needs to come to agreement on what are the consequential quality issues, which is a matter of judgement and reasonable people may differ. Because as described above, we are going to calibrate the urgency and stringency of our response based on that judgement. Some things will be blocked no matter what; others will be allowed through. If you set this dial to catch many trivial issues, you are putting needless drag on the team’s progress and also giving permission to care about things that do not matter.
My rule for more cosmetic matters in code was always that it’s more important to have and enforce a standard -- strict or loose -- and to automate that enforcement than it is to follow any given standard.
The braces guy still quit.
As noted above, you have a dozen, independent authors writing a novel together. How well do you think it will go if you “mess up” my favorite chapter? Especially if you work in a different time zone on the other side of the world and are now blissfully asleep while I deal with the consequences with my morning coffee? Blaming, like resentment, is toxic for a high-performing teams. While shifting left can catch a lot, if the culture of the team is continuous improvement of quality, you can sometimes see scapegoating of people who break things anyway. This should be seen as a chance to improve the tooling and a learning discussed blamelessly in a retrospective. There will still be grumbling, but this is not bad: the team should care about quality to a degree that motivates improvement, but not to a level where there’s coder-on-coder violence.
There is no tool you can adopt that will help you here; there is still No Silver Bullet. And as noted under hazards, shifting left will be hard. Some of the future essays in this blog will provide some templates and practices, but if you do one and only one thing as you sit down to do work, it is to make a small change in this direction. The improvements compound over time and the improved productivity creates more capacity to get there.
The concept “left of launch” comes from missile defense: in the timeline, the events that happen before the missile launch are to the left; this is where the idea of Shift Left in QA testing comes from as well. You can apply this idea to building code, unit testing, benchmarking and even security scanning: the common thread is that in all these things when there is a mistake of some kind, you want it to become apparent as soon after the mistake is made as possible. Why you might want that, and why it’s so important for a certain class of problems, is what we’ll be covering in this blog post.
I am devoting a short blog post to this idea because it’s going to come up again and again in this series, and rather than repeating myself I am just going to link back here. It’s also because this is about programming philosophy and programmer psychology more than technology or techniques, and it’s going to be a different kind of post than most -- and may not be everyone’s cup of tea.
This essay’s ideas do not apply to every programming project, and in fact there are some projects for which this is a terrible approach. It specifically applies to systems delivering value where unexpected failure has material consequences, so you can immediately rule out prototypes, spikes and student projects. It also requires that the blast radius for a failure is knowable at least approximately: for instance, that it’s OK to lose 30 cents, and not OK to lose $30K, or that you can tolerate one out of a million clients retrying a connection due to an error but not 50 of them.
Put more simply, we are in a classic engineering problem domain here. We cannot talk about whether something is built well or over-engineered without knowing what are the engineering tolerances; it’s not a sensible conversation. And of course, engineered things are built for a purpose and there are consequences when they fail.
In a software engineering project that respects the Left-of-Launch principle, you cannot deploy code containing a mistake whose consequences exceed the blast radius. In fact, you cannot make forward progress on any action if the consequences of that action go beyond the agreed threshold for that stage. Which means:
some code commits or pushes may be blocked
some code merges may be blocked
some code promotions may be blocked
Importantly, this is a calibrated escalation. The probability of blocking should be a function of the consequences of failing to block the change and the window of time afforded to recognize and repair the mistake, as well as the cost. Big consequences that afford you no time to respond thus have much stricter controls than small consequences that you can fix at your leisure. And wherever possible trivial issues should be addressed at point of discovery at zero cost, e.g. with a pre-commit auto-fix.
This section could also be titled ‘what bad looks like.’ If we push out code that we all know to have significant flaws and if propagation of those flaws have consequences, we are engaging in magical thinking:
we think no one will notice
we think it might not be so bad
we think the Magical Code Fairies will come by later and clean up the mess
The labor of the Magical Code Fairies includes fixing compiler warnings; removing TODO items that are not tracked in Jira; fixing known bugs that are not tracked in Jira; adding test coverage; and, for the truly damned spirits of that realm, data scrubbing.
They are mythical creatures.
The cost of defects escalates over time; this is well known. But I’d like to add to this that how you pay also matters. if the cost of quality control is amortized across the development process -- five minutes of additional work per pull request rather than an hour clean-up task -- it has a second-order productivity impact. For developers, long blocks of quiet time are essential for building the most complex components but due to coordination costs as teams increase in size, developers get fewer of these long blocks in a week. Do you really want to spend one of those open hours cleaning up warnings?
If you shift quality problem detection further left, it gets closer to the person who caused the problem. This increases accountability: you break it; you fix it, while at the same time ensuring the person who takes responsibility for the fix has the maximum context to help with a swift resolution. But in team psychology, there is an even greater win if you can ensure a culture of owning problems.
It’s not entirely true that the Magical Code Fairies are a myth. They do exist, but they have a tendency to burn out. You probably have worked with someone who took on all sorts of thankless tasks in a coding project … for a time. But this person probably got angry and frustrated for taking this on, perhaps at the expense of making more visible progress on other parts of the system, and moved on.
Resentment is the single most demotivating feeling. It should have no place in a high-performing team, which is why in Shift-Left we not only avoid blaming, but we also -- via accountability -- ensure that mistakes are fixed by people who made them, as they make them, not someone else later.
Ideally, this also motivates investments in ensuring that preventable things which are low-impact annoyances are resolved automatically rather than making it a person’s job. Better to use that capacity to add one EngProd developer so it is no one’s job!
Years ago I was talking to a developer on a team that used a monorepo for development of a complex trading system -- fairly rare at the time. They made a major release every Friday and 2 - 3 small patches a week, which in a bank environment was eye-opening for me. Everyone checking in code globally to one codebase, with high stakes, and they were delivering multiple times a week? I don’t think the term continuous deployment was even in use at the time, but that’s what they had. In his opinion, this was due to one and only one thing: their investment in automated testing meant they were not afraid to release, and slowing down to write tests let them move faster. This radical idea has become more common over time, but time and again I am told that investments in test automation, better builds, etc. is a tax on progress, when the opposite is the case: underinvestment in such things is the tax.
Again, there is a second-order value to getting this right. Teams that are less afraid of change of any kind are more willing to undertake the radical changes needed to get to the next level. We have all been there: I would do this refactoring, upgrade this library, swap out this server, but I don’t know what will happen. This fear also saps productivity, because it excuses build-up of technical debt.
One of the finest engineering managers I ever worked for had a rule about continuous improvement: he would leave a small portion of time for internal investment in every project. While the motivating benefits outlined here drove most of that investment, he pointed out another psychological piece: software development is neither art nor science, it’s craftsmanship -- the intersection of the two -- and pride in a piece of work is an essential motivating factor for individuals and teams. Delivery is a point of pride, but the things no one will ever see and that only a master builder will fully appreciate instill a deeper and more lasting pride than anything else.
If we achieve all these things from Shift-Left -- lower cost; accountability for mistakes; fearlessness about change; and pride in what we build -- we will be more productive and move faster. This is the essential argument being made in this essay, namely that investing in this approach and sometimes blocking progress makes you go faster. The trade-off between speed and quality is maybe not a complete myth, but it’s overdone.
As we are talking about engineering, it’s worth talking about the trade-offs of this approach, because it also involves trade-offs.
In the short run, eating the seed corn fills your stomach as well as the crop, and you can have it straight away. This is not an easy discipline for anyone on the team or for the broader organization. Pragmatism requires that we must violate these principles in a crisis, and in small, fast-moving organizations like a startup, crisis is always with us. There is always a good reason to ignore all of the above, and maybe your investors tell you that if you just make this delivery and get out to market the money will rain down and this can be used to hire as many Magical Code Fairies as you need to rewrite it. And here’s the worst part: they are not wrong; we need to live to fight another day as our first priority, before all of the above.
You will not win every argument on this one. The point of this essay is to arm you with the arguments for why you must win more of them over time to keep the organization healthy. It’s a struggle that requires persistence, and faith that it’s worth doing.
One developer’s trivial quality concern is deeply offensive to another; I remember one threat of quitting over the placement of braces in Java code, and we should bear in mind that creation is an emotional matter, and a large software codebase is like a 1000 page novel written by a dozen different authors, in parallel, and under pressure: the human mind under stress can fixate on trivial matters. Thus another hazard with shifting left is it can feed this tendency. The team thus needs to come to agreement on what are the consequential quality issues, which is a matter of judgement and reasonable people may differ. Because as described above, we are going to calibrate the urgency and stringency of our response based on that judgement. Some things will be blocked no matter what; others will be allowed through. If you set this dial to catch many trivial issues, you are putting needless drag on the team’s progress and also giving permission to care about things that do not matter.
My rule for more cosmetic matters in code was always that it’s more important to have and enforce a standard -- strict or loose -- and to automate that enforcement than it is to follow any given standard.
The braces guy still quit.
As noted above, you have a dozen, independent authors writing a novel together. How well do you think it will go if you “mess up” my favorite chapter? Especially if you work in a different time zone on the other side of the world and are now blissfully asleep while I deal with the consequences with my morning coffee? Blaming, like resentment, is toxic for a high-performing teams. While shifting left can catch a lot, if the culture of the team is continuous improvement of quality, you can sometimes see scapegoating of people who break things anyway. This should be seen as a chance to improve the tooling and a learning discussed blamelessly in a retrospective. There will still be grumbling, but this is not bad: the team should care about quality to a degree that motivates improvement, but not to a level where there’s coder-on-coder violence.
There is no tool you can adopt that will help you here; there is still No Silver Bullet. And as noted under hazards, shifting left will be hard. Some of the future essays in this blog will provide some templates and practices, but if you do one and only one thing as you sit down to do work, it is to make a small change in this direction. The improvements compound over time and the improved productivity creates more capacity to get there.
No activity yet