Writing
Forward Engineering Isn’t New. So What’s Different Today?
Forward engineering is having a moment, but it is the oldest direction in software. After twenty years of building, migrating and leading teams, what I have learned is that the risk lives in the handoffs, not the typing.
By Ka Lun Chan · Software architecture · Architecture / Engineering judgment / Leadership
What forward engineering is
Every few years a familiar idea comes back with a new name, and forward engineering is having its turn. When I hear it described as a new way to build software, I smile a little, because I have been doing it my whole career and so has everyone else who has shipped anything.
Forward engineering is the ordinary direction of software work: from what the business needs, to requirements, to an architecture and design, to code, to a running system. Each step makes the previous one more concrete. It is the opposite of reverse engineering, which starts from an existing system and works backwards to recover its design and the rules it encodes.
When I co-founded a communications company, forward engineering was the job in its rawest form. A business idea about affordable calls for immigrant families became requirements about rates, balances and resellers. Those became an architecture of signaling, media, business logic and billing. That became servers I racked, a network I configured and code I wrote, and eventually a platform that people depended on. Nobody called it forward engineering. We called it building the thing.
The term is useful anyway, because naming the direction makes you notice the handoffs. Each arrow in that chain is a place where meaning can be lost: a requirement nobody wrote down, a design decision that lived in one person’s head, an assumption in the code that the business never agreed to. Most of the expensive problems I have seen in software started at one of those arrows.
- 01Business needWhere meaning gets lostA requirement nobody wrote down
- 02RequirementsWhere meaning gets lostA decision that lived in one head
- 03DesignWhere meaning gets lostAn assumption the business never agreed to
- 04CodeWhere meaning gets lostA system nobody owns
- 05Running system
Forward need → requirement → design → code → productionReverse production → behaviour → design → requirements
Waterfall, Agile, iterative: still forward
People sometimes treat forward engineering as a polite word for waterfall: write all the requirements, then all the design, then all the code, in that order, once. Waterfall is one way to do forward engineering, and usually a bad one for software whose requirements are still being discovered. It is not the definition.
Agile and iterative development are forward engineering in small slices. A user story is a requirement. The conversation in planning about how to build it is design. The pull request is implementation, and the release is the running system. The difference is that you go around the loop every week or two instead of once a year, and you let what you learn in production change the next requirement. I wrote about why the slices should be small in smaller user stories and pull requests; the short version is that a small slice keeps every arrow in the chain short enough to check.
None of these approaches excludes the others. On the startup, the architecture of the voice platform was decided early and deliberately, because changing how calls were routed and billed later would have been painful, while the reseller dashboard was built in small iterations because we were still learning what resellers needed. That mix is normal. Some decisions are expensive to reverse and deserve design up front. Others are cheap to change and deserve to be discovered. Part of engineering judgment is telling them apart.
Forward and reverse engineering
Most of the forward engineering I have done in the second half of my career started with reverse engineering. A legacy system is a set of requirements nobody wrote down, expressed in code. Before you can rebuild it, you have to read it back out.
| Forward engineering | Reverse engineering | Where it shows up in a modernization |
|---|---|---|
| Requirements to design | Code back to requirements | Reading a legacy service to learn which rules the business still depends on |
| Design to code | Code back to design | Drawing the real architecture of a system nobody documented |
| Code to running system | Running system back to behaviour | Watching production traffic to learn what callers actually use |
| Schema to data | Data back to schema | Finding the meaning of a column three teams have used three ways |
The public-service modernization I describe in a case study is a good example. A government service ran on an ageing system that residents depended on every day, so switching it off to rebuild was not an option. The first job was reverse engineering: learning what the old system did, including the rules that had become important precisely because nobody remembered adding them. Only then could we forward-engineer replacements. We put an API facade in front of the legacy system and replaced one resident journey at a time behind it, with accessibility and security checks as release gates and the infrastructure in Terraform so the agency team could own it after hand-over.
The same pattern showed up elsewhere. Moving a publishing platform off WordPress onto Ruby on Rails meant first understanding how years of plugins and templates had shaped the content, then designing a structure that kept what readers relied on. Moving the communications platform from one data center to cloud regions meant mapping what depended on what before moving anything, then migrating one region at a time with a way back at every step. In every case the reverse half took longer than people expected, and it was where the risk lived. A migration fails on the behaviour you did not know you had.
For junior engineers, this is the most useful thing I can say about legacy code: treat it as evidence, not as an embarrassment. Somebody made each of those decisions for a reason. Some reasons are gone, and some are still holding up the business.
What is actually changing
The direction has not changed. The speed of one step has. AI coding tools and specification-driven development compress the arrow from design to code: describe the behaviour precisely, and a working first implementation appears in minutes rather than days. That is real, and I use it.
What it does not compress is the arrows on either side. Turning a vague business need into a requirement precise enough to build is still a conversation with people. Choosing an architecture that the team can operate, that fails in ways you can reason about and that costs what the business can carry is still judgment. And turning code into a reliable running system is still testing, deployment, monitoring and someone answering the page. Specification-driven development is, at heart, a renewed insistence on the first arrow: if the specification is good, the code follows. If it is vague, you now get vague code faster.
Are we more productive?
Producing code faster is not the same as delivering value faster, and the gap between the two is where teams get hurt. Every line still has to be reviewed, tested, secured, deployed and maintained by someone. If code arrives three times faster and the review process stays the same, review becomes the bottleneck, and the temptation is to skim. If tests are generated alongside the code from the same misunderstanding, they pass and prove nothing. Security review that was sized for a human pace of change falls behind. And maintenance is forever: a codebase that grew twice as fast has twice as much to understand when something breaks at night.
I think about this the way I think about capacity on a network. Speeding up one link does not speed up the path; it moves the congestion to the next hop. A team that wants real productivity gains has to widen the downstream steps too: smaller changes so review stays meaningful, tests written from the requirement rather than from the implementation, security checks in the pipeline, and a clear owner for every component. I made the same argument about delivery in what sprint carryover is telling you, where the work that slips is usually work stuck downstream of the part everyone measured.
What it asks of engineering leaders
If implementation gets cheaper, everything around it matters more, and most of that is a leader’s job.
Clear requirements come first. A vague goal is a risk, and it is cheapest to fix in week one. I want every engineer able to say what problem their work solves and how we will know it worked, which is the practice I describe as turning ambiguity into executable work.
Architecture decisions belong in writing: the options, the choice, and what we gave up. A decision record is forward engineering’s memory. It is also the thing that makes reverse engineering unnecessary for the next team, which is a gift worth giving.
Accountability needs a name. Every service, pipeline and scheduled job needs someone who owns it, who gets the alert, and who can say why it behaves the way it does. Code with no owner is a future archaeology project.
Production reliability is the end of the chain and the part the customer sees. Fast delivery of a system that falls over is not delivery. Release gates, monitoring, rollback and a sustainable on-call rotation are part of done, not a phase after it.
What I have learned
I started on carrier networks, where every change was forward engineering with a technician on the phone and no undo button. I built a platform from a business idea to racked servers to working software, and then moved it to the cloud one region at a time. I have re-platformed systems, restructured a startup’s mobile app, network and voice platform, and modernized public services that could not go down. And I have led teams through all of it.
The tools changed constantly. The chain did not. The work still runs from need to requirement to design to code to production, and it still breaks at the handoffs. What I have come to value most is keeping the whole chain honest: requirements someone agreed to, designs someone wrote down, code someone reviewed, systems someone owns. That has been true with every generation of tools I have used, and I expect it to be true of the next one.
Short answers
What is forward engineering?
The ordinary direction of software work: from a business need to requirements, to an architecture and design, to code, to a running system, each step making the previous one more concrete. Reverse engineering runs the other way, from an existing system back to its design and rules.
Is forward engineering the same as waterfall?
No. Waterfall is one way to do forward engineering, all at once. Agile and iterative development are forward engineering in small slices: a story is a requirement, planning is design, the pull request is implementation and the release is the running system, repeated every week or two.
How do forward and reverse engineering work together in a modernization?
A legacy system is requirements nobody wrote down, expressed in code. You reverse-engineer it first to recover what it actually does, then forward-engineer replacements, often behind an API facade one journey at a time so the service stays up.
Does generating code faster make teams more productive?
Only if the downstream steps keep up. Faster code moves the bottleneck to review, testing, security and maintenance. Smaller changes, tests written from requirements, security checks in the pipeline and a named owner for each component are what turn faster code into faster delivery.
The direction is old. Keep the chain honest.
Forward engineering has always been the job: need, requirement, design, code, running system. What changes is how fast one step moves.
Speed at one step moves the bottleneck to the next. Clear requirements, written decisions, named owners and reliable production are what turn code into delivery.
That is what twenty years of building, migrating and leading have taught me to protect.