I am Lino
May 29, 2026

Security by design: stop patching holes you dug yourself

Posted on May 29, 2026  •  12 minutes  • 2382 words
Table of contents

Most of the “security problems” you end up patching at 3 a.m. aren’t accidents.

They’re decisions made months earlier under pressure, over a cup of coffee, with the unshakeable conviction that “we’ll fix it later.”

Security by design is exactly about that: building security into the development cycle from the start, instead of relying on the holy trinity of scanner + pentest + blind faith.

Let’s get concrete: threat modeling, core principles, mistakes we’ve been repeating since 2010, and how to stop shooting yourself in the foot with your own code.

Security by design != a last-minute look

The average developer’s security lifecycle is as predictable as a broken clock.

First you design the app thinking about features. Wonderful.

Then you build it thinking about deadlines, because project management has somehow turned “just rough estimates, don’t worry, they’re not binding” into “commitments carved in stone, because you said so.”

And just when the suffering seems to be over and everything is about to ship, someone pipes up quietly: “hey, did we run any security scans on this?”

Awkward silence. Someone stares at the ceiling. Someone opens a new browser tab.

The security by design approach proposes something radical: doing things in the right order.

You bake security requirements into the SDLC (Secure Development Lifecycle) from day one, you do threat modeling when you’re defining the architecture — not six months later with the app already in production and the CEO staring you down.

You also integrate static analysis, code reviews, and security tests into the pipeline, instead of crossing your fingers once a year during the annual pentest.

OWASP even has a full Secure by Design Framework dedicated to this: security embedded in architecture, design, code, and operations — instead of slapping patches on a structure that was never designed to hold up in the first place.

Threat modeling: think like an attacker before an actual attacker does

Threat modeling sounds very academic — like something you do in a blazer in front of a whiteboard, with a blue marker that always runs dry at the worst possible moment.

The reality is simpler: it means sitting down and answering four straightforward questions:

What are we building (diagrams, data flows, components)? What can go wrong? What are we doing about it? And are we actually doing it right, or are we just fooling ourselves?

Microsoft, OWASP, and pretty much every modern guide agree: threat modeling should happen in the early stages of the SDLC — in requirements and design — and be revisited every time the architecture changes significantly.

Disclaimer: I know that citing Microsoft as a security example is a bit like citing a politician as an example of integrity — sure, neither may actually practice what they preach, but they do talk about it quite a lot and quite well, which was the point I was trying to make.

Anyway, the concrete benefit is that you catch design flaws (poor separation of concerns, insecure data flows, sensitive data stored in plain text because hey, who’s ever going to break in?) before you’ve written a single line of code. Which means you can prioritize security measures based on actual risk — not based on what blew up on Hacker News this week.

What this avoids, among other things, is that conversation where — with the app already in production — someone discovers the entire data model was never designed to comply with GDPR.

Or that the permissions model assumes all users are good people with good intentions.

Basic mistakes we’re still making in 2026

OWASP has been publishing its Top 10 vulnerabilities list for years, and the fascinating part — or the devastating part, depending on how you look at it — is that the same flaws show up edition after edition, with the reliability of that one relative at Thanksgiving who always says the exact same things.

Security misconfiguration is the all-time champion: default usernames and passwords left unchanged, unnecessary ports and services exposed, admin panels and debug endpoints live in production because nobody remembered to take them down before heading to lunch.

Right next to it is catastrophic credential management: API keys and passwords hardcoded directly into source code, config files with plaintext secrets cheerfully committed to public repositories. Entire startups have discovered this way that their AWS keys had been indexed by Google for months.

Then there’s access control, as sturdy as a revolving door: admin routes accessible to anyone who knows the URL, APIs that blindly trust whatever the client sends — including an isAdmin: true field that no joke, this exists, it has happened, and is probably happening somewhere in the world right now.

And finally, the timeless classic of not updating dependencies with reported CVEs — sometimes months or years old — because “now’s not the time,” and it always ends up being the time in the worst way possible.

OWASP itself sums it up with the infinite patience of someone who has been saying the same thing for years: most of the risk comes from systematic design and configuration mistakes, not from ultra-complex spy-movie attacks with gadgets and a dramatic soundtrack.

12-Factor Apps, configuration, and secrets: don’t be your own worst enemy

The 12-Factor Apps manifesto became famous mostly for deployment and scalability reasons, but if you read it carefully, two of its factors are directly about security — and most people ignore them because they’re buried between “logs” and “processes” and aren’t exactly a riveting read.

The first is the config factor: store configuration in the environment, not in the code.

Credentials, endpoints, third-party keys — all of it should live in environment variables or a dedicated secrets store, not baked into the repository like a welcome note.

Storing usernames and passwords in source code is the most obvious violation of this principle and, at the same time, the most efficient way to end up in an incident report with your name prominently featured on page one.

The second is the backing services factor: treat databases, queues, caches, and other external services as attached resources, with proper credentials and permissions for each environment.

Combining this philosophy with solid IAM practices and data protection (secret stores, encryption in transit and at rest) is basically the bare minimum for any modern application that wants to stay out of the news for the wrong reasons.

CVE: the public catalog of screw-ups and how to actually use it

The CVE (Common Vulnerabilities and Exposures) system is the standard database for identifying and cataloging known vulnerabilities in software and hardware. In practical terms: each CVE is an identifier (CVE-2025-XXXX) that describes a specific vulnerability — whether it’s a buffer overflow, an SQL injection, or an authentication flaw that shouldn’t exist but does, complete with a name, a description, and an official case number.

MITRE and a network of CNAs maintain and assign those IDs, with the patience of people who have made cataloging other people’s mistakes their life’s work.

The most common mistake with CVEs isn’t ignoring them entirely — that would be too obvious a failure — it’s dismissing them with a “not exploitable in our case” without any real analysis to back it up, or simply having no process to review them on a regular basis.

The second is almost worse, because at least the first one implies someone actually read the description.

The sensible approach is to integrate dependency and container scanners into the pipeline (SCA, container scanning) and maintain a vulnerability management process: prioritize CVEs by severity and actual exposure, patch, document.

Not exactly exciting, but that’s what separates the teams that find out about a problem before an attacker does from the ones that find out by reading a tweet.

Pentesting, reviews, and security as a habit

Pentesting is not the final boss, and it’s not the seal of approval that magically turns an insecure app into a secure one. It’s just one layer in a security by design program, and treating it as the only validation mechanism is exactly like getting a blood panel once every five years and calling yourself in peak shape.

A well-run pentest helps you identify vulnerabilities you missed ( bugs, insecure configurations, real attack vectors you hadn’t considered), validate that your controls actually work the way you think they do, and meet the requirements of frameworks like PCI-DSS, HIPAA, or GDPR.

The key is doing it regularly (not just after an incident has already happened), scoping it properly, and making sure the findings actually make it into the backlog and get closed — instead of collecting dust in a nicely formatted PDF that nobody opens again until the next pentest.

All of this is complemented by static and dynamic analysis integrated into CI/CD and code reviews focused on security patterns.

And by logging and monitoring that detects anomalous behavior before it becomes an incident — not just logs that record the crash after it’s already too late to prevent it.

Security by design in practice: what you should already be doing

If you want to stop patching holes you dug yourself, the bare minimum has four stages and none of them are optional.

In requirements and design: include explicit security requirements from the start (encryption, auditing, permissions, regulatory compliance) and run threat modeling on critical features and systems before anyone has written a single line of code.

In implementation: follow secure coding guidelines , manage configuration and secrets properly, and treat security as an acceptance criterion — not as a footnote that will be revisited “when there’s time.”

In verification: SAST, DAST, dependency and container scanners in CI/CD, code reviews with a security checklist, and periodic pentests well-integrated into the improvement cycle — not as an annual traumatic event.

In operations: monitor security events and anomalous activity, maintain CVE and patch management processes, and run post-incident analyses with real corrective actions — not solemn promises that “this will never happen again.”

Epilogue: you’re already paying the price

If all of this sounds like a lot of work, it’s because it is.

Integrating security into the development cycle requires discipline, tooling, and a willingness to have uncomfortable conversations about risk when everyone would rather be talking about features.

It’s not “cool.” It doesn’t show up in the roadmap. It doesn’t generate impressive demos to show off to management and clients.

But here’s the trap: the cost doesn’t go away if you decide not to pay it now. It compounds.

Every decision made under pressure, every secret hardcoded “just temporarily,” every vulnerability management process postponed because there are more urgent things right now — all of that is a deferred bill that someone is going to have to pay.

Sometimes it’s you, at 3 a.m. Sometimes it’s a customer whose data ends up in a forum they’d rather not be mentioned in.

The real question isn’t whether security by design is expensive — it’s how much you’ve already spent fighting fires you started yourself, and whether that money would have been enough to prevent them.


Quick glossary (so nobody gets lost in the acronyms)

Because “I know what that is” means different things to different people — and in security, that has consequences.


Sources and references

Because “a coworker mentioned it in standup” is not a valid academic reference, even if it sometimes feels like it should be.

Follow me

I write and share opinions about technology, software development and whatever crosses my mind.