Skip to main content

Command Palette

Search for a command to run...

Thought Driven Engineering: The Happy Path (EPISODE 1)

Updated
13 min read

Hey there, Coding Chefs! 👨‍💻

Building solutions especially on the web can be strenous, time consuming, and sometimes frustrating especially when you have things like functional changes, design changes, flow changes etc. The intention of this article is to work you through a path to ensure that your code on the frontend(software engineering) in particular is agile, modular, changeable, maintainable and less frustrating for you.

In order to do that you need to put thought to what you’re doing from Day-One, when engineering a product, no matter how small, planning cannot be an after-thought. You need to think about the feature, the different ways of doing the same thing, the caveats, pros, cons etc.

Without further ado, let’s get cracking (or cooking),

You know that feeling when you complete a task exactly the way you planned it?
When you write down "I'll build feature X using approach Y," then you actually execute it smoothly, and everything just... works?

There's something deeply satisfying about that experience. It's not just about getting the job done - it's about the confidence you build in your problem-solving abilities. It's proof that you didn't just stumble into a solution; you thought it through, planned it out, and executed it with intention.

But let's be honest - how often does that actually happen?

If you're like most developers (including past me), your typical workflow probably looks more like this: Get a task, open VS Code, start typing, hit a wall, Google frantically, try a different approach, hit another wall, refactor everything, and somehow arrive at a working solution three hours later, wondering how you even got there.

I used to live in that chaotic cycle. I thought I was being productive by jumping straight into code, but I was actually just creating more work for myself. Every "quick fix" became a debug marathon. Every "simple feature" turned into a refactoring nightmare.

Then I discovered something that changed everything: the power of thinking before coding.

Now, before you roll your eyes and think "obviously you should think first," hear me out. I'm not talking about the casual "hmm, let me think about this for 30 seconds" approach. I'm talking about a deliberate, structured way of approaching problems that consistently leads to what I call "the happy path" - where your implementation matches your plan, your code works the first time, and you feel genuinely confident in your solution.

The Satisfaction of Intentional Development

Here's what I've learned: when you want to build something and you write down exactly how you'll do it, then successfully execute that plan, you don't just solve the immediate problem. You build something much more valuable - confidence in your ability to think through complex problems systematically.

It's the difference between feeling like you got lucky and knowing you're skilled.

When you approach a problem with "I'll build X using Y approach because Z," and then you actually execute that plan successfully, there's a level of satisfaction that random trial-and-error can never provide. You start to trust your own thinking process. You become confident in tackling bigger, more complex problems because you know you have a reliable way to break them down.

But this confidence doesn't come from just thinking harder. It comes from having a systematic approach that you can rely on, every single time.

The Problem with "Code First, Think Later"

Early in my career, I was the king of jumping straight into implementation. Got a task? Great, let me start coding immediately! I thought planning was just a waste of time that kept me from the "real work."

I remember building a chat platform called "Lauchat" for university students. I was so eager to start that I dove straight into writing HTML and CSS. No planning, no component thinking, just pure excitement and determination. The result? I ended up copying and pasting the same code everywhere, creating a bloated, unmaintainable mess that was impossible to update or extend.

But here's what really happened every time I skipped planning: I'd spend 30 minutes "saving time" by jumping to code, then waste 3 hours debugging issues that proper planning would have prevented. I was essentially building a house without blueprints and wondering why the roof kept falling down.

The turning point came when I realized that the code is not the hard part - the thinking is. Anyone can type JavaScript syntax, but not everyone can think through edge cases, state management, and system interactions before they become problems.

That's when I developed what I now call "Thought Driven Engineering" - a simple framework that consistently leads to that satisfying "happy path" experience.

The Framework: Think, Jot, Decide, Code

This isn't some complex methodology with fancy diagrams. It's a simple, sequential process that forces you to solve problems in your head before you solve them in code.

Step 1: Think (No Code Editor Allowed)

When you get a new task, your first instinct might be to open your editor and start typing. Don't.

Instead, sit with the problem. Really sit with it. I like to eat pure bliss chocolates, step away from my computer, stare out, and just think.

Let me give you a real example. Let's say you're asked to build a countdown timer that counts down from a user-defined time, like 30 minutes.

Start by asking yourself the fundamental questions:

  • What's the simplest way to implement this?

  • What's the most complex way I can imagine it being done?

  • How might this feature evolve in the future?

But don't stop at the obvious stuff. Dig deeper:

  • How will I manage the state of the countdown?

  • What happens if the user refreshes the page?

  • What if they close their laptop and open it again 20 minutes later?

  • Should the timer pause or keep running in the background?

  • How do I prevent two different tabs from interfering with each other?

  • Should I use the user's device time or server time?

  • What about timezone issues or daylight saving time?

This is where most developers rush to code, but these questions are exactly what you need to think through first. Every "what if" scenario you consider now is a bug you won't have to fix later.

Real-world thinking session: When I was building a traffic light timing app (yes, really!, more details below), I spent a good hour just thinking about the problem:

  • How do traffic lights actually work?

  • Are the intervals consistent or do they vary?

  • What if the timing changes during different hours?

  • How do I account for the time it takes me to walk to my car?

  • What if I'm running late and need to adjust the calculation?

That hour of thinking saved me days of rebuilding.

Step 2: Jot (Brain Dump Everything)

Once you've thoroughly thought through the problem, start writing everything down. And I mean everything.

Don't worry about making it pretty or organized yet. Just get all your thoughts out of your head and onto paper (or a notes app).

For our countdown timer example, your notes might look like this:

Countdown Timer - Brain Dump

Core functionality:
- User inputs time (30 mins)
- Timer counts down to zero
- Show time remaining in MM:SS format
- Alert/notification when time reaches zero

State management challenges:
- Need to track: current time, target time, running/paused state
- Should persist across page refreshes
- Handle multiple tabs by segregrating cache keys (if using browser storage to persist)

Technical considerations:
- setInterval vs requestAnimationFrame vs Date-based calculation?
- Local storage for persistence
- Web API for notifications
- What if user changes system time?

Edge cases:
- Invalid input (negative numbers, non-numbers)
- Timer running when page isn't visible
- Browser sleep/wake cycles
- Multiple instances of the timer

Future features (might need):
- Multiple timers
- Preset times
- Sound customization
- Timer history

The key here is to capture everything you thought about in step 1. Don't filter or organize yet - just dump.

Step 3: Decide (Choose Your Battle Plan)

Now comes the decision phase. Look at everything you've jotted down and start making concrete choices about how you'll implement the solution.

This is where you:

  • Choose your technical approach

  • Decide which edge cases to handle now vs later

  • Plan your component structure

  • Identify external dependencies

  • Set your scope boundaries

For our timer example:

Implementation Decisions:

Technical approach:
✅ Use Date-based calculation (more accurate than setInterval)
✅ Store target time in localStorage
✅ Single timer for now (multiple timers = v2)
✅ Use browser's wakelock to keep the device active(and prevent sleep)

State structure:
- targetTime: Date object
- isRunning: boolean
- timeRemaining: number (calculated)

Components needed:
- TimeInput (for setting duration)
- TimerDisplay (shows current countdown)
- TimerControls (start/pause/reset)

Libraries needed:
- None! Keep it vanilla for now

MVP scope:
✅ Basic countdown functionality
✅ Persistence across refreshes  
✅ Pause/resume capability
❌ Multiple timers (future)
❌ Preset times (future)
❌ Sound customization (future)

Edge cases to handle:
✅ Invalid input validation
✅ Browser tab visibility changes
❌ Multiple tab synchronization (complex, move to v2)

Notice how I'm making explicit decisions about what to include and what to defer. This prevents scope creep and keeps me focused.

Step 4: Code (Finally!)

Only now do you open your code editor. But here's the beautiful part - by this point, you've already solved the hard problems. The coding becomes almost mechanical because you know exactly what you're building and why.

Your implementation becomes clean and purposeful because you've thought through the architecture:

class CountdownTimer {
  constructor(containerId) {
    this.container = document.getElementById(containerId);
    this.targetTime = null;
    this.isRunning = false;
    this.intervalId = null;

    this.loadFromStorage();
    this.render();
    this.startUpdateLoop();
  }

  setDuration(minutes) {
    // Input validation (planned in step 3)
    if (!minutes || minutes <= 0) {
      throw new Error('Duration must be a positive number');
    }

    // Date-based calculation (decided in step 3)
    this.targetTime = new Date(Date.now() + minutes * 60 * 1000);
    this.saveToStorage();
  }

  // Rest of implementation...
}

Because you planned everything out, you're not making random decisions mid-implementation. You're not googling "how to handle timer edge cases" because you already thought through the edge cases. You're not refactoring your state structure because you already designed it.

Real-World Example: My Traffic Light App

Let me share how this framework played out in a real project.
I was tired of always hitting(not the same as running red lights) red lights near my house, so I decided to build an app to time them perfectly such that I can get to all the lights while they’re green.

Think Phase (1 hour): I sat outside with a notebook and actually observed the traffic lights. I timed them, looked for patterns, and thought about variables:

  • Lights have different timing during rush hour vs off-peak

  • There's a pedestrian crossing that affects timing

  • I need to account for walking time to my car

  • Weather might affect how fast I walk

Jot Phase (30 minutes): I wrote down everything I observed:

  • Red light: 2.5 minutes during rush hour, 2 minutes off-peak

  • Green light: 1.5 minutes consistent

  • My walking time: 45 seconds normal, 60 seconds if raining

  • Buffer time needed: 15 seconds for unexpected delays

Decide Phase (30 minutes): I chose a simple approach:

  • Manual time input (not trying to be too smart)

  • Account for current time of day

  • Simple notification system

  • Mobile-first design (using it while getting ready)

Code Phase (2 hours): Because I'd planned everything, the coding was straightforward. No major surprises, no architectural changes, no "wait, I didn't think about this" moments.

The result? An app I still use daily that has saved me countless minutes of sitting at red lights.

Why This Framework Works

1. It prevents tunnel vision: When you start coding immediately, you often get fixated on the first solution you implement, even if it's not the best one.

2. It catches edge cases early: It's much easier to handle edge cases in the design phase than to try to fit them into existing code (obviously leading to more problem like breaking existing feature that’s currently working).

3. It reduces decision fatigue: By making architectural decisions upfront, you don't waste mental energy on them during implementation.

4. It creates better documentation: Your notes become natural documentation for future you (or your teammates).

5. It makes debugging easier: When something goes wrong, you have a clear map of your thinking process to trace back through.

Common Objections (And My Responses)

"This feels like overthinking simple features"

Trust me, I used to think the same thing. But here's what I learned: there are no "simple" features in production applications. Even a basic button has considerations around accessibility, error states, loading states, and user feedback.

"Planning takes too long"

In my experience, planning takes about 30 - 35%(up to 65%, on a system design of an entire app) of the total time you'll spend on a feature. But it saves you 50% of the debugging and refactoring time. The math works out strongly in favor of planning.

"What if requirements change?"

Good planning actually makes you more adaptable, not less. When you understand the problem deeply, you can respond to changes more intelligently. You know what's core to the solution and what's just implementation detail.

Adapting the Framework

This framework scales from small features to large projects:

For small features (< 1 day):

  • Think: 15-30 minutes

  • Jot: 10-15 minutes

  • Decide: 10-15 minutes

  • Code: The rest

For medium features (1-3 days):

  • Think: 1-2 hours

  • Jot: 30-60 minutes

  • Decide: 30-60 minutes

  • Code: The rest

For large projects (1+ weeks):

  • Think: Half a day to a full day

  • Jot: A few hours across multiple sessions

  • Decide: Half a day, possibly involving team discussions

  • Code: The rest, with periodic re-evaluation

Making It Stick

The hardest part isn't learning this framework - it's building the discipline to use it when you're excited to start coding.

Here are some tricks that helped me:

1. Close your code editor: Literally close VS Code when you get a new task. The temptation to "just peek at the existing code" will derail your thinking process.

2. Use a timer: Set a timer for your thinking phase. Don't let yourself touch code until it goes off.

3. Share your notes: Send your "Jot" phase notes to a teammate or rubber duck. Explaining your thinking helps solidify it.

4. Celebrate good planning: When a feature goes smoothly because you planned well, take a moment to appreciate that. Positive reinforcement helps build the habit.

The Compound Effect

Here's the thing that really sold me on this approach: the benefits compound over time.

When you consistently think through problems before coding, you start recognizing patterns. You get better at spotting potential issues. Your "Think" phase gets faster and more thorough simultaneously.

You also build a reputation as someone who writes solid, well-thought-out code. Your code reviews become faster because you've already considered the edge cases. Your estimates become more accurate because you understand the scope upfront.

When to Break the Rules

This framework isn't religious doctrine. There are times when you might skip or compress steps:

  • Prototyping/spikes: When you're exploring feasibility, feel free to code while thinking, don’t code without thinking regardless.

  • Tiny bug fixes: For obvious one-line fixes, don't overthink it

  • Well-understood patterns: If you've built the same type of feature 20 times, you can lean on experience. But don’t forget, you’re getting better too, so your solution should not be the same as the first time, which means your implementation should be better, which leads us back to thinking about the first solution, how has it panned out, did it stand the test of time? What can you improve. All of this is “still” thinking.

But even in these cases, I find that a quick mental run-through of the framework helps.

Wrapping Up

The "Think, Jot, Decide, Code" framework has fundamentally changed how I approach software development. It's transformed me from someone who writes code to someone who solves problems (and then expresses those solutions in code).

The next time you get a feature request, I challenge you to try this approach:

  1. Think through the problem completely before touching your keyboard

  2. Jot down everything you've considered, no matter how small

  3. Decide on your approach, scope, and architecture

  4. Code with confidence and clarity

You might feel like you're moving slower at first, but I promise you'll end up moving much faster in the long run. Your code will be cleaner, your bugs will be fewer, and your stress levels will be lower.

Remember: Great engineering isn't about how fast you can type code. It's about how well you can think through problems.

A new day, another opportunity to think before you code! 🚀

J

Lovely writing as always.

Do you sometimes find it kind of a chore to code the solution after the thinking and documentation? If so, how do you handle that?

O

I usually don't, but I understand that some people sometimes have code feet. I'd say anytime that happens take a step back, review your solution and documentation again, think about any edge case you may have missed, think about other solutions to the same problem. The idea is to look at your train of thoughts long enough to get your motivation to actually "write code" as you only solved the problem in theory and not yet in practical.
My advise will be to take time, review your solution, think about the impact of your solution (cost/time wise) to you and to the users/consumers, and find motivation to do the work.

1

More from this blog

C

Cracked Chefs by Oluwaferanmi Adeniji

20 posts

Battle-tested Coding patterns, Javascript wizardry, System Design, Product Engineering and Management, and architectural secrets.