Engineering process

Every "Small Change" Has a Story

"Can you just add one small feature?" Why a change that looks tiny from outside is rarely small in the code — and what keeps software easy to change.

Upwwards Engineering Build & Support teams 8 min read

“Can you just add one small feature?”

It is a sentence every developer has heard. On the surface it sounds simple. Add a button. Introduce a new field. Create a report. Integrate another payment method.

But software is not a collection of isolated screens. It is a connected system where one seemingly harmless change can ripple through authentication, business logic, APIs, databases, integrations, caching, and user experience.

That is why what looks like a small room to a client can feel like removing a supporting wall to an engineer.

Two-panel illustration. Top, labelled CLIENT: 'Can you just add one small feature?' beside a neat house with an arrow pointing to empty ground marked 'just one small room'. Bottom, labelled DEVELOPER: 'Sure, just one small change…' beside the same house collapsed into rubble, with arrows labelled Legacy Code, Hidden Dependencies, Untested Logic and Friday Deployment. Caption: every small change has a story.
Neither person in this picture is wrong. They are describing different halves of the same request.

Here is the short version, before the long one: a small feature is small only if the system around it is healthy. The feature is the visible part. Everything that keeps the feature from breaking the other forty things sharing the same data is the part nobody sees, and it is the part that sets the timeline.

Why small features aren’t always small

Imagine asking a builder to add one room to your house.

If the foundation is strong, the work is straightforward. If the structure is old, undocumented, and modified several times over the years, that same request might require reinforcing walls, moving plumbing, updating electrical wiring, and proving the whole building is still safe to live in.

The room is identical in both cases. The job is not.

Software works the same way. A small feature often means working around:

  • Legacy code nobody wants to touch, because the person who wrote it has left and it has no tests
  • Hidden dependencies between modules that only reveal themselves when something breaks
  • Outdated libraries that must be upgraded first, dragging in unrelated changes
  • Untested business logic, where the only way to know if you broke a rule is to ship it and wait
  • Performance bottlenecks that a new query quietly turns from slow into unusable
  • Security implications, because a new field is a new thing to validate and a new thing to leak
  • Database migrations that have to run against live data without losing any of it
  • Third-party integrations whose behaviour you can observe but cannot control

The feature itself might take a few hours. Making sure it does not break everything else is where the real engineering happens.

What “just add a button” actually touches

It helps to make this concrete. Take a genuinely small request: add an “Export to CSV” button to the orders list.

The button is thirty minutes of work. Here is the rest of it.

What the request looks likeWhat the work actually is
Add a buttonDecide who is allowed to press it — can a support agent export another company’s orders?
Export the ordersDefine which orders. The ones on screen, or every order matching the filter, including the 400,000 the user cannot see?
As a CSVGenerate it without loading 400,000 rows into memory and taking the server down
Download itDeliver a file that takes 40 seconds to build, through a request that times out at 30
DoneDecide what happens when two people press it at once, what gets logged, and whether the file containing customer data is now sitting in a public bucket

None of that is padding, and none of it is an engineer being difficult. Every line is a question that has to be answered by someone. The only real choice is whether it gets answered deliberately now, or accidentally in production later.

This is also why “how long will it take?” is the harder question and “what does this touch?” is the more useful one. The second question can be answered in an hour and makes the first one honest.

Technical debt is like structural damage

Technical debt is not just messy code.

It is undocumented decisions, rushed releases, shortcuts taken to meet deadlines, and years of “we will fix it later.” Each one is a small, rational trade at the time it is made. The problem is that nobody writes them down, so nobody ever pays them back.

Eventually every new feature starts taking longer, because the system has become fragile. That is when development slows down — not because the developers got slower, but because the software became harder to change safely.

The distinction matters, because the two problems have opposite solutions. Slow developers is a hiring conversation. A system that is risky to change is an engineering one, and adding more people to it usually makes it worse.

Some honest signals that you are paying interest, not building:

  1. Estimates keep growing for work that sounds similar to work you did last year.
  2. Engineers ask for investigation time before they can estimate anything at all.
  3. There is a part of the system everyone agrees not to touch.
  4. Deployments happen when one specific person is available.
  5. Bugs come back — the same bug, in a slightly different place.
  6. New hires take months, not weeks, to ship anything on their own.

None of these are moral failures. Every long-lived system accumulates some of this. What separates a healthy codebase from a fragile one is not the absence of debt — it is whether anyone is tracking it and paying it down on purpose.

That is a large part of what a maintenance retainer is actually buying: not just bug fixes, but the steady, unglamorous upkeep that stops the interest from compounding.

What makes a change actually small

The flip side is worth stating, because it is the part that is achievable. Systems where small changes stay small are not the ones with the cleverest code. They share four ordinary properties.

Tests you trust. Not total coverage — coverage of the rules that would cost you money if they broke. Their real value is not catching bugs; it is that they let someone change code they did not write without being afraid.

A schema you can change. Migrations that live in the repository, run automatically, and can be run again on an empty database. If you cannot recreate your data model from files, you cannot safely change it.

One obvious place for each thing. When a rule about pricing lives in exactly one function, changing pricing is a small change. When it lives in four, it is a research project first and a change second.

A deploy anyone can run, and reverse. If shipping is a button and rolling back is the same button, the cost of being wrong drops enormously — and the cost of being wrong is most of what makes teams slow.

None of this is exotic. All of it is cheaper to build in as you go than to retrofit later, which is the entire argument for doing it in the first place.

Good engineering is invisible

Users rarely notice when software is well engineered.

They notice when it crashes. They notice when the login stops working. They notice when payments fail.

Most of the work that keeps software reliable happens where nobody is looking: refactoring, testing, monitoring, documenting, reviewing code, and improving architecture. It never appears in a release note. It has no screenshot.

This creates a genuinely difficult problem, and it is worth naming plainly. The work that prevents outages is invisible, and the outages it prevents are invisible too. A team that does this well looks, from the outside, like a team with an easy system. A team that skips it looks fast — right up until the quarter where everything takes three times as long and nobody can explain why.

These are not extra tasks. They are what allow software to keep growing without collapsing under its own weight.

Build software that can grow

The goal is not just to launch software. It is to build software that can evolve.

Every new feature should make your product better — not make the next feature twice as difficult. That is the actual test of an architecture, and it is measurable: if your tenth feature took longer than your third, something in the foundation is asking for attention.

This is why sustainable engineering treats maintainability as seriously as delivery. It is also why we document requirements and user flows before writing code — the decisions you write down once are the ones you do not have to re-derive, differently, every time someone new touches the system.

Because software is never truly finished. It grows with your business.

What to ask before you call it small

If you take one practical thing from this, take these four questions. They cost a few minutes and routinely save weeks.

  1. What does this touch? Not how long — what other parts of the system read or write the same data.
  2. Who is allowed to use it? Permission questions answered late become security incidents answered publicly.
  3. What happens when it fails? Every feature has a failure path. If nobody designed one, the users will find it.
  4. How do we undo it? If the answer is “we don’t,” the change is not small regardless of its size.

A good engineering team will not resent these questions. They will be relieved you asked.

Final thoughts

The next time someone says “it’s just one small feature,” remember that every feature touches a larger system.

Great software is not measured by how quickly it is built. It is measured by how confidently it can be improved.

At Upwwards we build software that does not just launch — we build software designed to grow, adapt, and stay reliable as your business evolves. And when a system has already become hard to change, we fix that too, without starting over.


Software getting slower to change? A maintenance retainer covers the upkeep that keeps it fast — or tell us what you’re working on and we’ll give you an honest read.

Questions we get asked

The short answers.

Why does a small feature take so long to build?
Because the feature is rarely the work. Writing the button, field, or report is often a few hours. The rest is making sure it behaves correctly for every kind of user, does not break the features that already depend on the same data, survives a deployment, and can be reversed if it goes wrong. In a well-maintained codebase that surrounding work is small. In a fragile one it can be ten times the size of the feature itself.
What is technical debt, in plain terms?
It is the accumulated cost of every shortcut, undocumented decision, and postponed cleanup in a codebase. Like financial debt it is not inherently bad — taking some on to hit a launch date is a legitimate trade. It becomes a problem when nobody tracks it and nobody pays it down, because the interest is charged on every future change you make.
How can I tell if our software is becoming hard to change?
Watch the estimates, not the code. If work that used to take two days now takes a week, if engineers ask for time to investigate before they can estimate at all, if releases cluster around one person who knows how deployment works, or if the team keeps saying "we should not touch that part" — those are all symptoms of a system that has become risky to modify.
Should we rewrite the system or fix what we have?
Fix what you have, in almost every case. Rewrites restart the clock on every bug you have already found and fixed, and they usually take far longer than estimated because the old system's behaviour was never fully documented — that lack of documentation being the same reason the rewrite felt necessary. Targeted refactoring of the parts that actually hurt is nearly always cheaper and less risky.
Is it unreasonable to ask for a small feature?
Not at all, and no good engineering team will treat it that way. Asking is how software improves. The useful shift is from "how long will this take" to "what does this touch" — that single question surfaces the real scope early, before anyone has committed to a date that was never achievable.
patchlane FREE · 48 HOURS

Somewhere around Day 30?

Send us the repository. Within 48 hours you get a written audit — what is actually broken, what is fine, what to fix first, and what it takes to make it production-ready. No rewrite proposal, no obligation, and you keep the report whether or not you hire us.

Keep reading