...
...
August 7, 2026

Building Features is a Trap. Steal This Idea From Mario 64.

Most dev teams are stuck in a feature factory, creating brittle, one-off solutions. But as a classic video game shows, focusing on core, composable primitives is the key to building adaptable and powerful software.

architecturebest practicesdeveloper toolsproduct managementtechnical debt
V
VooStack Team
August 7, 2026
9 min read
Building Features is a Trap. Steal This Idea From Mario 64.

Your backlog is a list of features, not a strategy. You ship one, then the next, and the velocity chart looks good for a while. But your codebase gets heavier, more tangled, and slower. Every new feature feels harder to build than the last because it has to account for the weird exceptions of the ten features that came before it. You're not accelerating. You're accumulating debt.

As a recent post highlighted on Hacker News points out, the design of the classic game Super Mario 64 holds a critical lesson for software teams. The game doesn't have hundreds of special-case moves for specific situations. It has a handful of core, composable primitives: a jump, a double jump, a long jump, a wall kick, a dive. And from these few moves, players create an incredible depth of emergent gameplay. This isn't just good game design. It's a blueprint for better software architecture.

Most teams are building single-purpose features. The best teams are building core software primitives that combine to solve problems they haven't even thought of yet.

The Feature Factory vs. The Primitives Playground

Let's make this concrete. Imagine a marketing team needs a new landing page for a product launch. They come to the engineering team with a mockup and a user story.

The Feature Factory approach is to take the request literally. You open up your CMS, maybe Contentful or Sanity, and create a new content type called ProductLaunchPage. You add fields that match the mockup exactly: heroTitle (Text), heroImage (Image), featureSectionOneHeader (Text), featureSectionOneBody (Rich Text), and so on. It's a perfect 1:1 mapping. You ship it in a week. Success.

Two weeks later, the team needs a case study page. The layout is similar, but not identical. So you create another content type, CaseStudyPage, and duplicate most of the fields. Then they need a partner page. And a webinar signup page. After a year, you have twenty different page types. They share almost no logic, and changing the font on the heroTitle requires updating twenty different templates. The system is brittle and bloated.

The Primitives Playground approach looks different. You look at the landing page request and you don't see a page. You see a collection of smaller, reusable ideas. You see the core software primitives.

You build:

  • A Hero component with props for title, subtitle, image, and CTA buttons.
  • A TextBlock component for generic formatted text.
  • A TwoColumnLayout component that can accept other components as children.
  • An ImageGallery component.

Instead of a rigid ProductLaunchPage type, you create a flexible page builder that allows a content editor to stack these primitives in any order. Now when the marketing team wants a case study page, they don't need an engineer. They just assemble the existing primitives in a new way. When they ask for a new feature, like a video embed, you build one VideoEmbed primitive and it's instantly available on every page, past and present. You've given them Mario's long jump and wall kick, and they're finding new ways to cross gaps you never designed for.

This is the fundamental shift: stop building nouns (ProductLaunchPage) and start building verbs (addHero, addTextBlock).

How to Identify Your Core Primitives

This all sounds great in theory, but how do you find these magical primitives in your own complex domain? It's about looking for the true, underlying actions your users (or your own systems) need to perform.

At AgileStack, when we consult with teams drowning in technical debt, we often run an exercise. We look at their last six months of user stories and pull out all the verbs. What are users actually doing?

  • "As a user, I want to filter the transaction list by date range..."
  • "As an admin, I want to filter the user list by account status..."
  • "As a marketer, I want to filter customers by their last purchase date..."

The repeated verb is filter. The Feature Factory builds three different filtering UIs. The Primitives Playground builds one powerful, generic filtering and data-grid system. It takes longer to build the first time, but the second and third requests are practically free. That's leverage.

Primitives Aren't Just UI Components

This concept applies to the entire stack. For an API like our MailStack product, the primitives are obvious: send, track, template. The power isn't in a single function that sends a welcome email. The power is in giving developers these fundamental building blocks to create any workflow they can imagine.

For a developer tool like DevStack, the primitives are the CLI commands and their flags. devstack deploy --env=production and devstack deploy --env=staging aren't two features. They're one primitive (deploy) with a parameter (--env). Good CLI design is all about creating orthogonal, composable commands, not monolithic scripts.

Your primitives are the atomic units of work in your domain. Find them, name them, and build them to be composed.

The Real Tradeoffs of Building This Way

Building with primitives isn't a free lunch. It represents a tradeoff, shifting work to the present to save massive effort in the future. You need to be honest about the costs.

First, there's the upfront design cost. Building a reusable Filter component is harder than hardcoding a filter for a single database table. You have to think about its API. What props will it take? How will it handle different data types (strings, numbers, dates)? How does it return the selected filter state? This requires architectural thinking, not just implementation. It can easily double the time for the first feature that uses it.

Second, there's the abstraction risk. You can get it wrong. A primitive that's too generic can become a configuration nightmare. A primitive that's too specific isn't actually reusable. We once built a generic Table component that tried to handle every possible use case: sorting, filtering, pagination, inline editing, virtual scrolling. It ended up with over 50 props and was so complex that no one on the team understood how to use it. We eventually threw it out and built smaller, more focused primitives: a SortableHeader and a PaginatedContainer. The lesson was that primitives themselves can be composed of smaller primitives.

Finally, there's the maintenance burden. A core primitive is a critical internal dependency. A bug in your Button component might show up in 100 places. This means your core primitives need a higher level of testing, documentation, and release management than a one-off feature screen. They are the foundation of your house. They have to be solid.

A Backend Example: The Notification System

Let's move this to the backend. A product needs to send notifications: welcome emails, password resets, comment mention alerts, and weekly summary emails.

The Feature Factory approach creates four distinct functions:

// DO NOT DO THIS
function sendWelcomeEmail(user) { ... }
function sendPasswordResetEmail(user) { ... }
function sendCommentMentionEmail(user, comment) { ... }
function sendWeeklySummaryEmail(user, data) { ... }

This seems fast at first. But soon, you need to add SMS notifications. Do you create four more functions? What about push notifications? What happens when you want to change your email templating library from Handlebars to EJS? You have to refactor every single function.

The Primitives Playground approach defines the core concepts of a notification first.

  1. A Message primitive: A data structure defining a notification's content, independent of how it's delivered.
  2. A Transport primitive: An interface that knows how to deliver a Message.
  3. A Template primitive: A system for rendering the message body.

Here's what that might look like in pseudocode:

// The core data primitive
interface Message {
  recipient: string; // email, phone number, device token
  subject?: string;
  body: string;
  metadata: Record<string, any>;
}

// The core action primitive
interface Transport {
  send(message: Message): Promise<{ success: boolean; id: string }>;
}

// A concrete implementation of the Transport
class MailgunEmailTransport implements Transport {
  async send(message: Message) {
    // Mailgun API specific logic here
    // ...
    return { success: true, id: '...' };
  }
}

// Another concrete implementation
class TwilioSmsTransport implements Transport {
  async send(message: Message) {
    // Twilio API specific logic here
    // ...
    return { success: true, id: '...' };
  }
}

Now, your application logic doesn't call sendWelcomeEmail. It assembles a Message and hands it off to a Transport. Adding a new notification type, like "Invoice Paid", is just a matter of creating a new template. Adding push notifications is just a matter of creating a new FirebasePushTransport class. You can switch from Mailgun to SendGrid by changing one dependency injection. You've built a system that is resilient to change because its core primitives are decoupled and composable.

What This Means For Your Team

Moving from a feature factory to a primitives playground isn't just a technical change. It's a cultural one. It requires product managers, designers, and engineers to think differently about the requests they make and the solutions they build.

Here are the key takeaways:

  • Stop building pages, start building systems. Look for the reusable patterns beneath every feature request.
  • Identify your domain's core "verbs". These are the actions that provide the most leverage. Is it filtering, displaying, notifying, or calculating? Build those verbs as robust, reusable services or components.
  • Embrace the upfront cost. Acknowledge that building a good primitive takes more time initially. Frame it as an investment that will pay dividends in future development speed and system stability.
  • Primitives unlock emergent features. Just like in Mario 64, when you give users a few powerful, composable tools, they will invent solutions and workflows you never anticipated.

The next time your team scopes a new feature, don't just ask "what's the fastest way to build this?" Ask a better question: "What's the reusable primitive hiding inside this request?" Answering that question is the difference between a codebase that gets slower every week and one that gets more powerful with every commit.


Building something in this space? AgileStack helps teams ship enterprise-grade software without the consulting-firm overhead. Book a 30-minute call and tell us what you're working on.

Topics
architecturebest practicesdeveloper toolsproduct managementtechnical debt
Authored by
V

VooStack Team

Engineering, VooStack

The VooStack engineering team. A veteran-owned, SDVOSB-certified software house building Flutter, .NET, and cloud-native products end to end, from San Antonio, TX and Oklahoma City, OK.

Share this article