Skip to Content
Design Philosophy

Design Philosophy

This document captures how we think about building software at CardNexus. It’s not about specific technologies or patterns — it’s about the principles that guide our decisions.

Core Principles

KISS — Keep It Simple, Stupid

The simplest solution that works is almost always the right one.

Complexity is a cost. Every abstraction, every indirection, every “clever” solution adds cognitive load for the next person who reads the code — including your future self. Before adding complexity, ask: what concrete problem does this solve today?

In practice:

  • Prefer boring, obvious code over clever code
  • Use standard library solutions before reaching for dependencies
  • Write code that reads like prose, not puzzles
  • If you need comments to explain what the code does (not why), simplify the code

YAGNI — You Aren’t Gonna Need It

Don’t build for hypothetical futures.

It’s tempting to add flexibility “just in case” — config options nobody asked for, abstractions for cases that might never happen, extension points for features on no roadmap. This is almost always a mistake.

In practice:

  • Build exactly what’s needed for the current requirement
  • Don’t add parameters, options, or configurability unless explicitly needed
  • Delete code paths that aren’t used
  • Resist “while I’m here, I might as well…” additions

The best code is code you didn’t write. Every line is a liability — it must be read, understood, tested, and maintained.

DRY — Don’t Repeat Yourself (With Judgment)

Duplication is sometimes better than the wrong abstraction.

DRY is often misapplied. The goal isn’t to eliminate all repetition — it’s to ensure that knowledge has a single source of truth. Two pieces of code that look similar but represent different concepts should not be forced into a shared abstraction.

In practice:

  • Extract when you see the same concept repeated, not just similar syntax
  • Three instances of duplication is a signal to consider extraction — not a rule
  • If an abstraction requires many parameters or conditions to handle “variations,” it’s probably wrong
  • Inline an abstraction if it’s used in only one place

Simple and Safe Beats Clever

Especially under time pressure, favor the straightforward path.

Near deadlines, during incidents, or when stakes are high: this is not the time for architectural innovation. Make the smallest change that solves the problem. Reduce blast radius. Ship something you can explain in two sentences.

When in doubt:

  • Can you explain this change to someone in 60 seconds?
  • What’s the worst thing that could happen if this breaks?
  • Is there a simpler approach you rejected too quickly?
  • Would you be comfortable debugging this at 2am?

Ownership and Accountability

You own what you ship. Fully.

When you merge code, you’re not just the author — you’re the person responsible for it working correctly, for understanding why it works, and for fixing it when it doesn’t.

This means:

  • You can explain any code you ship — the design decisions, the tradeoffs, the failure modes
  • You surface problems early — blocked? uncertain? risky? Say so before it becomes a crisis
  • You see it through — shipping isn’t done when the PR merges; it’s done when it works in production

“I thought it would work” is not a defense. Neither is “it worked on my machine” or “the tests passed.”


Working with AI

AI tools are powerful accelerators. We use them extensively. But they change how fast you can write code, not who is responsible for it.

The Ownership Rule

Using AI is encouraged. Shipping code you don’t understand is not.

When you use AI to help write code:

  • You must understand every line before committing it
  • You must be able to explain the design decisions as if you made them
  • You must be able to defend the tradeoffs in code review
  • You must be able to debug it when it breaks

If someone asks “why did you do it this way?” the answer cannot be “that’s what the AI suggested.” The answer must be your reasoning for why this approach was correct.

AI Does Not Reduce Your Responsibility

AI is a tool, like an IDE or a linter. It doesn’t make decisions — you do. This means:

What AI doesWhat you’re responsible for
Generates code quicklyReviewing every line for correctness
Suggests implementationsEvaluating whether the approach fits the codebase
Offers multiple solutionsChoosing the right one for the context
Makes mistakes confidentlyCatching those mistakes before they ship

The “Explain It” Test

Before shipping any AI-assisted code, you should be able to:

  1. Explain the design — Why this approach? What alternatives did you consider?
  2. Identify the tradeoffs — What are the downsides? What did you give up?
  3. Describe failure modes — How could this break? What happens if inputs are unexpected?
  4. Trace the flow — Walk through the code line by line and explain what happens

If you can’t do these things, you don’t understand the code well enough to ship it.

Practical Guidelines

Do:

  • Use AI to draft implementations, then review and refine
  • Use AI to explore approaches, then choose deliberately
  • Use AI to write tests for code you wrote
  • Use AI to explain unfamiliar code in the codebase
  • Use AI to generate boilerplate you understand

Don’t:

  • Copy-paste AI output without reading it carefully
  • Ship code that “seems to work” but you can’t explain
  • Use AI to avoid learning — if it generates something unfamiliar, understand it first
  • Blame AI when something breaks (“the AI wrote that part”)
  • Let AI make architectural decisions for you

Complexity and AI

AI tends to generate more code than necessary. It often suggests abstractions, utilities, and “helpful” additions that you didn’t ask for.

Your job is to pare it down. Ask:

  • Do I need all of this?
  • Is there a simpler version that solves the actual problem?
  • Am I adding complexity because AI suggested it, or because it’s genuinely needed?

The best AI-assisted workflow is: generate → understand → simplify → ship.

AI makes it easy to generate code. It doesn’t make it easy to generate good code. That’s still your job.


Summary

  1. Keep it simple — complexity is a cost, not a feature
  2. Build what’s needed — not what might be needed someday
  3. Own what you ship — understand it, explain it, fix it
  4. AI is a tool — you are responsible for the output
  5. Favor safety — especially when stakes are high

These principles compound. Simple code is easier to own. Code you own is easier to keep simple. Tools that help you go faster are valuable only if you maintain quality.

Build things you’re proud of. Ship things you understand.

Last updated on