
Most engineering teams have said it. Some version of: we know there are rough edges, but we'll clean them up in the next sprint. We'll do a proper security review before we go to production. We'll tighten things up once the feature ships.
That sentence is one of the most expensive things a team can say. Not because it is dishonest: teams that say it usually mean it. It is expensive because the belief underneath it is structurally false: that security is something you add to code, rather than something that either exists in the code or does not. The gap between understanding that distinction intellectually and actually building it into how a team operates is where most application vulnerabilities originate.
The dominant mental model in software development treats security like paint. You build the structure first (get the thing working, ship the feature, hit the deadline) and then apply the protective coating afterward. A penetration test here. A dependency audit there. A SAST tool bolted onto the pipeline as a final check.
This feels reasonable because it mirrors how we think about other quality concerns. Performance optimisation often does happen after the fact. UI polish often happens in a later pass. Security does not work this way.
Security is a property that emerges, or fails to emerge, from the structural decisions made at every layer of a codebase. How data flows between components. How state is managed and validated. How errors are handled. How external inputs are treated. How authentication context is passed and verified. These decisions are made constantly, thousands of times across a codebase, most of them by individual developers under time pressure, and very few of them are easily revisited after the fact. When you defer security to a later phase, you are not postponing a task. You are compounding decisions that will each need to be individually unpicked, evaluated, and corrected. The cost does not stay flat while you wait.
If the 'fix it later' model is broken, why do intelligent, experienced engineering teams keep reaching for it?
Deadline pressure is the first reason. When a product milestone is two weeks away and the team is behind, treating security as a discrete future task is genuinely seductive, it feels responsible, like you are scheduling it rather than ignoring it. But scheduling a review of a completed codebase is reconstruction, not architecture. The decisions that determine security are being made right now, while everyone is focused on shipping.
The second is false confidence from tooling. Automated scanning tools (like static analysis, dependency checkers, or secret detection) are valuable. They are good at finding known patterns of known problems. They are not good at identifying the architectural decisions that create the conditions for vulnerabilities to appear. A codebase can pass every automated check and still be deeply flawed because the problem is not in any individual line of code, it is in how the system is structured. A clean scan means no known vulnerabilities were found. It says nothing about whether the system is actually secure.
The third is how most teams are organised. Security becomes a specialised function (a team, a role, a review gate) rather than a shared engineering standard. This produces handoff thinking: developers write code, security reviews it. On paper it looks like a process. In practice it means security is evaluated retrospectively, after the decisions that determine it have already been made. Every developer who does not sit in the security function feels implicitly absolved of responsibility for it.
Reframing security as a property of code quality rather than a downstream process requires being specific about what that property consists of.
Secure code does less than it could. Functions do one thing. Interfaces expose only what needs to be exposed. Data flows through the smallest number of components necessary. Every additional touchpoint where external input reaches system state is a potential vector. Teams that default to simplicity as a posture, not as an aesthetic preference, are harder to attack than teams that default to generality.
Closely related is how data is handled. Insecure code is characterised by implicit assumptions: that a value has already been validated somewhere upstream, that a type is what it appears to be, that an input can be trusted because it came through an authenticated endpoint. Secure code validates explicitly, handles errors explicitly, and makes no assumptions about the provenance or format of data crossing a boundary. That is also just good engineering.
Underneath both of these is ownership. In systems that have evolved quickly, one of the most common sources of vulnerability is ambiguity about who is responsible for checking what. Is this endpoint authenticated? Has this operation been authorised for this user in this context? Codebases where these checks are distributed inconsistently, present in some paths, absent in others, duplicated in ways that eventually diverge, have a structural problem that scanning will not catch. The check needs to happen in one place, enforced by the architecture.
The test suite should reflect all of this. A suite that covers happy paths but not error conditions, not boundary inputs, not edge cases, has documented what the system does when everything goes right. Security vulnerabilities almost always live in what happens when things go wrong.
None of this is an argument against automated tooling. Static analysis, dependency auditing, secret scanning, and dynamic testing all have genuine value and should be part of any serious pipeline. The argument is against using them as a substitute for architectural discipline rather than a complement to it.
The right frame: tooling catches what slips through, not what is structurally wrong. A secret detection tool catches a hardcoded credential accidentally committed. It does not catch a credential management pattern that is fundamentally insecure. A dependency auditing tool catches a library with a known CVE. It does not catch the architectural decision to pull in forty external dependencies for a service that could have been written with three. A static analyser catches a SQL injection pattern in a specific function. It does not catch the broader absence of input validation discipline across the codebase.
Tooling should be positioned early (in the IDE, in the pre-commit hook, in the pull request gate) because its value is highest when it catches problems closest to when they were introduced. That is a tactical question. The strategic question is whether the team has the architectural discipline that makes the tooling's job manageable. A tool that flags three hundred issues in a codebase has not solved a security problem. It has made one visible.
The 'fix it later' conversation is usually framed in terms of risk: what might happen if a vulnerability is discovered and exploited. That framing is legitimate but too abstract to change behaviour in most teams.
The more immediate cost is remediation overhead that accumulates as structural problems compound. Code written without security discipline is harder to reason about, harder to test, harder to refactor, and harder to extend. Every new feature built on top of an insecure foundation either inherits the insecurity or requires working around it, and working around structural problems in code is expensive in a way that does not show up in sprint velocity or feature delivery metrics. It shows up in slower delivery, more production incidents, and engineering time consumed by firefighting rather than building.
A codebase with six months of deferred decisions does not require six months of remediation distributed evenly. It requires a concentrated effort to understand decisions made under pressure by people who may no longer be on the team, in a codebase that has grown since, affecting a system now in production with real users.
The engineering teams that consistently ship secure software are not the ones that take security most seriously at the end of the development process. They are the ones that have stopped treating it as a distinct phase.
In a PR review on a team that has internalised security, the question "has this input been validated?" gets asked in the same breath as "does this function do one thing?" Security is not a checklist that appears before release, it is the standard the code is held to throughout. Not a specialised concern owned by a specific function, but a shared expectation distributed across everyone writing code.
That is not achievable by scheduling a security review later. It is only achievable by building security into how the team thinks about quality itself. The 'we'll fix it later' conversation is worth having honestly, because later has a cost, and it is rarely the cost teams are imagining when they say it.
Morphotech works with engineering teams on the architectural decisions that determine security posture before a line of code is written. If that is the stage you are at, get in touch.