Claude Code Plugins

Community-maintained marketplace

Feedback

Designing Before Coding

@obra/clank
25
0

Design in pseudocode first, iterate approaches, then translate to code

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name Designing Before Coding
description Design in pseudocode first, iterate approaches, then translate to code
when_to_use Before implementing any non-trivial routine or class. When jumping straight to code. When code feels hack-y or you're unsure how to proceed. When you catch yourself in "just one more compile" cycle. When tempted to code first and figure it out later.
version 1.0.0
languages all

Designing Before Coding

Overview

Write the design in pseudocode BEFORE writing implementation code. Iterate through multiple approaches in pseudocode, pick the best, THEN translate to code.

Core principle: Once you start coding, you get emotionally involved with your code and it becomes harder to throw away a bad design. Design is cheap to change; code is expensive.

Violating the letter of the rules is violating the spirit of the rules.

When to Use

Use for ANY programming task beyond trivial one-liners:

  • New features or functionality
  • Non-trivial bug fixes
  • Refactoring
  • Any routine longer than ~5 lines
  • When you're unsure how to proceed
  • When solution feels complex

Red flags that you need this:

  • Jumping straight to implementation code
  • "Just one more compile" syndrome
  • Coding yourself into a corner
  • Losing train of thought mid-implementation
  • Forgetting to write parts of a routine
  • Staring at screen not knowing where to start
  • Code feels hack-y or patched together

Don't skip when:

  • Under time pressure (designing first is FASTER than debug cycles)
  • Problem seems simple (simple problems benefit from design too)
  • You're confident you know the solution (overconfidence causes mistakes)

The Process

Step 1: Check Prerequisites

Before any design work:

  • Is the job well-defined?
  • Does it fit cleanly into overall design?
  • Is it actually required by the project?
  • What information will this routine hide?
  • What are the inputs and outputs?
  • What are preconditions (guaranteed true before routine called)?
  • What are postconditions (guaranteed true after routine returns)?

If unclear, STOP. Get clarification before proceeding.

Step 2: Name the Routine

Name it BEFORE designing internals.

If you struggle to create a good name = WARNING SIGN.

  • Vague name = vague design
  • Wishy-washy name = wishy-washy purpose
  • Can't name it clearly? Back up and clarify what it should do.

Good name = clear, unambiguous, describes what routine does.

Step 3: Think Through Error Handling

Before writing pseudocode:

  • What could go wrong? (bad input, invalid return values, etc.)
  • How will this routine handle errors?
  • Does architecture define error strategy? Follow it.
  • Which error approach: return neutral value, substitute valid data, log warning, return error code, throw exception, shut down?

Step 4: Research Libraries and Algorithms

Don't reinvent the wheel:

  • Check standard libraries
  • Check company/project libraries
  • Check algorithm books if complex
  • Reuse good code > writing from scratch

Step 5: Write High-Level Pseudocode

Use your code editor (it will become comments):

# Write general statement of purpose first
This routine outputs an error message based on an error code
supplied by the calling routine. The way it outputs the message
depends on the current processing state, which it retrieves
on its own. It returns a value indicating success or failure.

# Then write high-level pseudocode
set the default status to "fail"
look up the message based on the error code

if the error code is valid
   if doing interactive processing, display the error message
   interactively and declare success

   if doing command line processing, log the error message to the
   command line and declare success

if the error code isn't valid, notify the user that an internal
error has been detected

return status information

Pseudocode characteristics:

  • Uses plain English, not programming language syntax
  • Describes WHAT to do, not HOW to do it (intent, not implementation)
  • High-level enough that you could implement in any language
  • Low-level enough that translating to code is mechanical

Step 6: Review the Pseudocode

Critical step - don't skip:

  • Read it mentally - does it make sense?
  • Explain it to someone else (or imagine explaining it)
  • Does it handle all cases?
  • Is the logic clear?
  • Are there edge cases missed?

If hard to explain or unclear, iterate the pseudocode. Don't proceed to code.

Step 7: Try Multiple Approaches (Iterate)

Don't settle for first design.

Try 2-3 different approaches in pseudocode:

  • Different algorithms
  • Different data structures
  • Different control flow
  • Different error handling strategies

Compare them:

  • Which is simpler?
  • Which is more maintainable?
  • Which handles edge cases better?
  • Which is easier to test?

Pick the best BEFORE coding.

Step 8: Translate Pseudocode to Code

Only after pseudocode is solid:

  1. Write routine declaration
  2. Turn header comment into language comment
  3. Turn each pseudocode line into a code comment
  4. Fill in code below each comment
  5. Keep comments (they explain intent at higher level)
def report_error_message(error_code):
    """
    This routine outputs an error message based on an error code
    supplied by the calling routine. The way it outputs the message
    depends on the current processing state, which it retrieves
    on its own. It returns a value indicating success or failure.
    """
    # set the default status to "fail"
    status = Status.FAILURE

    # look up the message based on the error code
    error_message = lookup_error_message(error_code)

    # if the error code is valid
    if error_message.is_valid():
        # determine the processing method
        processing_method = current_processing_method()

        # if doing interactive processing, display the error message
        # interactively and declare success
        if processing_method == ProcessingMethod.INTERACTIVE:
            display_interactive_message(error_message.text)
            status = Status.SUCCESS

        # if doing command line processing, log the error message
        # to the command line and declare success
        elif processing_method == ProcessingMethod.COMMAND_LINE:
            log_to_command_line(error_message.text)
            status = Status.SUCCESS
    else:
        # if the error code isn't valid, notify the user that an
        # internal error has been detected
        display_interactive_message("Internal Error: Invalid error code")

    # return status information
    return status

Notes:

  • Each comment expands to 1-10 lines of code
  • Comments kept to show intent
  • Code mechanical translation from pseudocode
  • If one comment explodes to many lines → extract to new routine

Step 9: Check the Code

  • Mentally execute each path
  • Check for off-by-one errors, initialization problems
  • Verify error handling works
  • Step through in debugger
  • Test with planned test cases

If routine is unusually buggy: start over. Don't hack around it.

Step 10: Clean Up

  • Remove redundant comments (if comment just restates obvious code)
  • Check variables are well-named
  • Verify routine does one thing well
  • Check for proper initialization
  • Verify all parameters used

Quick Reference

Stage Action Warning Sign
Prerequisites Define inputs, outputs, pre/postconditions Can't explain what routine does
Naming Clear, unambiguous name Struggling to name = design unclear
Errors Think through what could go wrong Forgetting error cases
Research Check libraries and algorithms Reinventing the wheel
Pseudocode Write high-level design in English Jumping to code syntax
Review Explain to someone else Can't explain clearly
Iterate Try 2-3 approaches Settling for first idea
Translate Mechanical conversion to code Fighting the language
Check Mental execution, testing "Just one more compile"

Common Mistakes

❌ Skipping pseudocode: "It's simple, I'll just code it" ✅ Fix: Even simple routines benefit. Takes 30 seconds, saves debugging time.

❌ Pseudocode too low-level: Writing programming syntax in comments ✅ Fix: Use plain English describing WHAT, not HOW. Should work in any language.

❌ Settling for first design: Implementing first idea that comes to mind ✅ Fix: Try 2-3 approaches, compare, pick best.

❌ Compiling too early: "Let me compile and see if it works" ✅ Fix: Don't compile until you're convinced it's right. Avoid hack-compile-fix cycle.

❌ Coding before understanding: "I'll figure it out as I code" ✅ Fix: If you don't understand at pseudocode level, you won't understand at code level.

❌ Vague name accepted: "I'll think of a better name later" ✅ Fix: Vague name = vague design. Fix the design NOW.

If You've Already Written Code

If you wrote implementation code without pseudocode first, you've violated the process.

No exceptions:

  • Don't keep it as "reference"
  • Don't claim "it works so it's fine"
  • Don't skip pseudocode now "to save time"
  • Don't "adapt" existing code while writing pseudocode
  • Don't look at your implementation while designing

Required steps:

  1. Set code aside - Don't delete yet, but don't look at it
  2. Write pseudocode from scratch - Describe what routine SHOULD do (not what it does)
  3. Try 2-3 alternative approaches - Design without bias from your implementation
  4. Pick the best approach - Based on simplicity, maintainability, clarity
  5. Compare with your implementation:
    • If your code matches the best design: keep it, add pseudocode as comments
    • If a different design is better: delete your code and re-implement from pseudocode

The hours already spent are sunk cost. Don't throw good time after bad by keeping suboptimal design.

"But it works!" doesn't mean it's the best design. Working code without good design is technical debt.

Production Emergencies: Pseudocode is Still Faster

Even with production down and $15k/minute at stake:

Time comparison:

  • Direct coding: 3 min code + 10 min debugging edge cases = 13 minutes
  • Pseudocode first: 2 min pseudocode + 3 min code = 5 minutes

The pressure makes pseudocode FEEL wasteful. It's not. It's faster.

For true 5-line fixes:

  • Pseudocode: 30 seconds (literally write 5 bullet points)
  • Review: 15 seconds (read the bullets)
  • Code: 2 minutes (mechanical translation)
  • Total: 3 minutes vs jumping to code and missing an edge case

Emergency doesn't override the principle. It reinforces it.

Debug cycles under pressure are even more expensive (stress, mistakes, production impact).

Common Rationalizations

Excuse Reality
"It works already" Working ≠ best design. Pseudocode reveals better alternatives.
"Keep code as reference" You'll bias toward it. Set it aside completely.
"Too simple to design" Simple code still benefits. Takes 30 seconds.
"Emergency, no time" Pseudocode is faster than debug cycles. Always worth 2 minutes.
"I designed in my head" Undocumented mental design = didn't happen. Must write it.
"Ship it, document after" Pseudocode-after doesn't catch design problems. Too late.
"Production down, skip process" Broken process = how production got broken. Don't compound it.
"Just this once" "Just this once" becomes "always this once." No exceptions.

Verification Checklist

Before marking work complete:

  • Wrote pseudocode BEFORE any implementation code
  • Tried at least 2 alternative approaches in pseudocode
  • Compared approaches and consciously picked best
  • Pseudocode turned into comments in final code
  • Can explain routine clearly using the pseudocode
  • If coded first: set aside, designed from scratch, compared approaches

Can't check all boxes? Return to Step 5 (Write pseudocode).

Red Flags - STOP and Design First

Before coding:

  • Jumping straight to implementation code
  • "I'll figure it out as I code"
  • "Let me try this and see if it compiles"
  • "Just one more compile"
  • Can't explain what routine does
  • Struggling to name something
  • Code feels hack-y
  • Coded yourself into a corner
  • Lost train of thought mid-coding
  • Forgot to implement part of routine

After coding (without pseudocode):

  • "It works so it's fine"
  • "Keep as reference while designing"
  • "I designed it mentally"
  • "Too late now"
  • "Ship it, document after"
  • "Emergency, can't afford to redo"

All of these mean: Stop coding. Return to pseudocode.

Benefits

  1. Fewer bugs - Design mistakes caught before coding
  2. Faster development - Less debug time than hack-compile-fix
  3. Better designs - Easier to try alternatives in pseudocode
  4. More maintainable - Comments explain intent, not just mechanics
  5. Less emotional attachment - Easier to throw away bad pseudocode than bad code
  6. Self-documenting - Pseudocode becomes comments explaining why

Integration with TDD

PPP and TDD work together. Both require design before implementation.

Combined process:

  1. Design test in pseudocode - What behavior should this verify?
  2. Translate test pseudocode to test code - Keep pseudocode as test comments
  3. Run test - Watch it FAIL (RED)
  4. Design implementation in pseudocode - Try 2-3 approaches
  5. Pick best approach - Based on simplicity, clarity
  6. Translate implementation pseudocode to code - Keep as comments
  7. Run test - Watch it PASS (GREEN)
  8. Refactor - Update pseudocode comments if logic changes

Order is critical:

  • Test pseudocode → test code → FAIL
  • Implementation pseudocode → implementation code → PASS

Both skills mandate: NO implementation code without design first.

TDD says write the test first. PPP says design the test in pseudocode before writing test code. They reinforce each other.

Real-World Impact

From Code Complete research:

  • Students using pseudocode scored 30% higher on comprehension
  • Names averaging 10-16 characters minimized debugging effort
  • 95% of errors are programmer errors (not compiler/hardware)
  • Complexity overload = applying irrelevant methods mechanically

Key quote: "The picture of the software designer deriving his design in a rational, error-free way from a statement of requirements is quite unrealistic. No system has ever been developed that way." - David Parnas

Design is sloppy and iterative. Pseudocode keeps that sloppiness cheap.