Good Code Isn't Fast. It's Ordered.
Why the CRISP framework puts performance dead last—and why that's the only way to ship software that actually works.

You just finished a function. It works. You're about to hit commit.
Wait.
Don't press Ctrl+Enter yet.
Ask yourself: if someone else reads this tomorrow, can they tell you what it does within one minute? If yes, great. But if their first question is "Does it actually do it correctly?" – what's your answer?
Good code isn't a destination. It's a set of choices, and those choices have a strict order. John Arundel's CRISP principles give us that order: Correct, Readable, Idiomatic, Simple, Performant. This order isn't a suggestion. It's a discipline.
1. Correctness: Don't trust tests. Don't trust yourself.
You wrote a hundred tests. They all pass green. You feel good.
But did you check the tests themselves? Where did those expected results come from? Did you translate them line by line from the requirements document, or did you just type "it should be like this" off the top of your head?
Incorrect high performance is more destructive than correct slow code. A wrong result, the faster it spreads, the more damage it does.
The only effective stance for correctness is adversarial suspicion. After you write a function, treat yourself as the enemy. Pass in 0. Pass in nil. Pass in a ridiculously huge number – can your function handle it? Call it ten times concurrently – does the data stay consistent? If an external API you depend on returns a field you never defined, does your code fail gracefully or panic immediately?
There's a more insidious trap: your test case itself might be wrong. You tested "addition returns the correct sum," but you forgot to test "addition with negative numbers." So don't embrace your test report. Interrogate it. Behind every green light, there should be a red-card scenario you actively designed to try and break your logic.
Only when you are certain it won't crash at 2 AM because of some hidden null pointer do you earn the right to talk about other qualities. Otherwise, you aren't writing code. You're writing a time bomb.
2. Readability: Pretend it's your first day on the job.
You can write two hundred lines a day, but you read two thousand. And your own code, one week later, looks like it was written by a stranger.
So readability isn't "adding comments." Readability is removing every obstacle that makes you pause.
One method works: zero-read.
Before you leave work, open today's file. Read it line by line from the top. Anywhere you stop for a second to think "what does this variable hold again?" or go back to count brackets before a nested if – drop a // TODO right there. Fix only those places tomorrow.
Good readability isn't flashy. It's transparent. Like a good conversation, you don't need to guess who is speaking – you naturally know who said what and when. Code is the same: variable names say what they hold, function names say what they return, structure says the flow. If the reader doesn't have to strain, your code succeeds.
3. Idiomatic: Stop inventing. Use the community's language.
Every language has its dialect. Go's if err != nil. Rust's ?. Python's list comprehensions. These aren't aesthetic choices. They are consensus to reduce friction.
You decide to write a "more elegant" variant – say, turning error handling into if err == nil { ... } else { ... }. Every time a reader sees that, their brain flips the logic once. One flip costs 0.1 seconds. Ten flips cost one second. A hundred flips cost ten seconds. A team touches thousands of lines a day. These tiny stalls accumulate and drain an entire day's patience.
Idiomatic conventions aren't handcuffs. They are defaults. You only override them when you have a sufficiently strong reason. Most of the time, that reason doesn't exist.
So read open-source projects. See how they name things, how they structure packages, how they handle errors. Align your writing to that baseline. Your code will graduate from "personal artifact" to "public infrastructure."
4. Simple: Write the clumsy version first. Then decide if you need abstraction.
"Simple" is the most misunderstood word. Beginners think simple means "fewer lines," so they aggressively extract shared functions and eliminate duplication. The result? Three layers of abstraction where changing one thing requires jumping across three files. Newcomers look at it and want to quit.
Real simplicity is direct and restrained.
Direct: The code's behavior matches its surface description. No hidden side effects.
Restrained: One module does one thing. Nothing extra.
How do you get there? Two steps.
Step one: Write the feature honestly. Even if three loops look similar. Even if validation logic repeats. Just make it work. Turn the tests green.
Step two: Stare at those three similar loops. Ask yourself: are their intentions the same? Or do they just look alike? If the intentions differ, keep the duplication. If the intentions are exactly identical, now extract the function. After extraction, ensure that someone reading only the call site understands what it does – they shouldn't have to jump into the implementation.
DRY (Don't Repeat Yourself) isn't a law. It's a result. When you truly understand the business domain, the right time for abstraction arrives on its own. Forcing DRY only creates unnecessary coupling.
5. Performant: Worry about it last. And worry with tools.
A programmer's intuition about performance is usually wrong.
You spend an afternoon optimizing a loop. In production, 95% of the time is spent on database queries. Those milliseconds you shaved off? Invisible.
Performance must come last. Not because it's unimportant, but because if the first four steps aren't solid, you don't have the right to talk about performance – you haven't even guaranteed correctness. What's the use of running faster if you're running wrong?
The correct approach: write correct, readable, simple, and idiomatic code first. Ship it. If it feels slow, open the profiler. It will tell you exactly where the bottleneck lives. You change those three lines, and often you get a 20x improvement. The rest of the codebase? You don't even touch a single comment.
Premature optimization is a time-killer and a source of code rot. Only when you have data proving the bottleneck exists should you perform localized surgery. That's the most responsible attitude toward your system's lifecycle.
The Final Checklist
Before your next git commit, run this order through your head:
Is it correct? – Did I deliberately try to break it?
Is it readable? – Did I zero-read it and count the stalls?
Is it idiomatic? – Would the community raise an eyebrow?
Is it simple? – Did I create abstractions just to "remove duplication"?
If all four pass, then I ask: is it fast? – If not, do I have profiler data?
Good code isn't talent. It's discipline, executed in the right order.
That function you just wrote? After passing through these five filters, it's ready to commit. When your colleague reads it tomorrow, they'll say, "This is comfortable to read" – not "What is this person trying to show off?"
That's the truth about good code. It doesn't rely on inspiration. It relies on order.
About the Creator
Jin
Writer of reamstories
https://reamstories.com/jin
Enjoyed the story? Support the Creator.
Subscribe for free to receive all their stories in your feed. You could also become a paid subscriber, letting them know you appreciate their work.
Comments
There are no comments for this story
Be the first to respond and start the conversation.