Skip to main content

🧰 Write It Ugly: Temporarily Letting Go of Engineering Discipline

If you’re reading this, you’ve probably spent years learning to:

  • Keep functions small and focused
  • Avoid global state
  • Name things clearly
  • Structure code for reuse and clarity
  • Prevent side effects

In short: you’ve learned how to engineer.

But when the timer starts on a Leetcode-style problem…

Sometimes, you have to throw half of that out the window.

This article squarely fits into the "The problem is simple — I just need to implement" category of Leetcode situations that I laid out in the introduction to the leetcode toolbox. Maybe it even drifts a little into the gray zone of "I started implementing, but now I’ve gotten a little twisted" — like when you realize you’re traversing a 2D array in the wrong direction and need to rethink your structure mid-stream.

And it highlights a deeper tension: when you’ve built a career on modular, composable, Unix-y design — with clean separation, minimal coupling, and future-proof extensibility — Leetcode feels like a jarring pivot.

In real software, system design is almost trivial. It’s just the obvious outcome of clear modular thinking. You often start small — a stub, a CLI, a narrow API surface — and grow it. You iterate. You evolve. You are building with intent.

I’ve tried Documentation-Driven Development and while I like it in principle, it sometimes forces too much premature structure. You outline flags, environment variables, edge cases — and suddenly your MVP has a wall of features to implement before it's even useful.

With noCRUD, for example, I knew from day one it would eventually be parallelized and support infrastructure provisioning. But I started by just getting a serial, no-provisioning prototype working — because even that was non-trivial. Only after building a serial version did I circle back and add the concurrency layer.

Leetcode doesn’t give you that luxury. You’re not writing with evolution in mind. Requirements don’t change. They don’t expand.

You’re not rewarded for long-term thinking — just for fast, short-term correctness.


🛠️ Leetcode Is a Different Game

These problems aren’t production-grade. They’re:

  • One-off
  • Highly scoped
  • Often with fixed constraints
  • Always under time pressure

You’re not building software. You’re producing output.

So now the goal is:

Get something correct and readable enough to submit — fast.


⚖️ The Engineering Tradeoff Spectrum

Instead of treating clean code vs. fast code as a binary, treat it like a dial:

Engineering HabitReal-World CodeLeetcode Adjustment
Avoid global statePass + return valuesJust mutate a global list
Extract helper functionsSRP, reusable unitsOnly extract for clarity at call site
Avoid nested logicBreak into flat flowsTolerate some nesting for implementation speed
Use descriptive namesget_students_by_classg() is fine for a throwaway map
Generalize structureParametrize itHardcode if it saves you time

You're not giving up on discipline — you're optimizing for urgency.


💡 Practical Examples

1. Mutating a Global

result = []

def process(student):
result.append(student.id) # instead of return + append

You could pass and return result. But… why? You’re only calling this in one place. The clock is ticking. It’s fine.



2. Debug-First, Clean-Later

debugMap = {}
for ...:
debugMap[...].append(...)

print(debugMap) # Looks messy — but now you trust your loop

This is scaffolding. You can clean it later — or not.


3. Variable Names: Clarity vs. Flexibility

# Real-world clarity
for student in students:
for score in student.scores:
...

# Leetcode speed
for i in range(n):
for j in range(m):
...

Is i and j better? Definitely not. But here’s the thing:

If halfway through, you realize you’re traversing columns instead of rows, at least you’re not spending 30 seconds renaming everything.

Use what lets you move quickly. If that means cryptic names you wouldn’t allow in a PR — go for it. If that means still writing student and score because that’s what makes sense to you — also valid.

There’s no purity here. Just pragmatism.


🧹 What You’re Practicing Is Not Just Coding — It’s Judgment

Real engineering is about:

  • Understanding constraints
  • Adapting to the problem space
  • Managing complexity
  • Delivering value with tradeoffs

So is Leetcode — just at warp speed.

Clean code is still the north star.

But here, it's okay to take a weird path to get there — or not get there at all, if the answer is due in 5 minutes.


🐚 Closing: It’s Not "Write Bad Code." It’s "Write Smart Code, Fast."

You’re not betraying your discipline. You’re just temporarily turning it down.

Think of it like writing in shorthand instead of calligraphy.

The key is knowing which rules you’re breaking — and doing it on purpose.

Or not.

I don’t know. I can’t say I’ve mastered this.