Article

How I think about building products as a developer, not just features

A practical breakdown of how I approach product-minded development: thinking about real users, systems, performance, failure states, and long-term quality instead of only shipping isolated features.

Most developers are trained to ship features.

A ticket says “add search,” so search gets added. A ticket says “add sync,” so sync gets added. A ticket says “add a form,” so the form appears on the screen.

There is nothing wrong with that. Shipping features is part of the job.

But after working on real products, I started caring much more about a different question:

Does this actually work for the person using it?

Not just technically. Not just in the happy path. Not just in the demo.

Does it feel clear? Is it fast enough? What happens when the connection is bad? What happens when the data grows? What happens when the user comes back next week and expects the product to remember what they did?

That is the difference between building a feature and building a product.

A feature can be complete.

A product has to hold up in real life.

Product-minded developer thinking from user moment to product system

The trap of feature-first development

Feature-first development feels productive because it creates visible progress.

A new page exists. A button works. A filter appears. A modal opens. A dashboard gets another chart.

Everyone can point at something and say: done.

But a feature can be “done” and still make the product worse.

Search can exist but return noisy results. Sync can exist but feel unreliable. A settings page can exist but make the product harder to understand. A dashboard can have five charts and still not help anyone make a decision.

I have seen this pattern enough times to become careful with it.

The problem is not that features are bad. The problem is that features are often planned as isolated pieces of UI instead of parts of a larger behavior.

A ticket might say:

Add saved places.

But the real product question is closer to:

How does someone save something important at the exact moment they need it, and how do we make them trust that it will still be there later?

That is a much better question.

It forces you to think about context, not only implementation.

The product is the behavior around the feature

The more I build, the less I care about feature names.

“Save place” is a feature.

The product is everything around it:

  • how quickly the user can do it
  • what happens if the phone has no signal
  • whether the user needs to type anything
  • how the app confirms the place was saved
  • how the place syncs later
  • how easy it is to find again
  • whether privacy is the default
  • whether the flow still works from a small screen or Apple Watch

That is how I thought about Pean, my private place-saving app for iPhone and Apple Watch.

The basic feature sounds simple: save a point on a map.

But that description misses the actual product problem.

Pean is for moments when someone finds a place they may want to return to later: a fishing spot, a mushroom area, a berry patch, a landmark, a quiet place in the woods. Those moments are often outside. The user might be moving. The connection might be weak. They might not want to take out the phone, open a form, choose a group, type a note, and organize everything perfectly.

So the product cannot start with a complex form.

It has to start with capture.

Save first. Organize later.

That one decision changes the product much more than it changes the feature list.

I try to understand the usage moment first

Before I think too much about components, APIs, or database structure, I try to understand the moment where the product is used.

That sounds obvious, but it is easy to skip.

A desktop dashboard, a mobile app, a browser extension, and a marketing page all have different usage moments. They may use the same technologies, but they do not need the same product decisions.

For every product surface, I try to ask:

  • where is the user?
  • are they focused or distracted?
  • are they in a hurry?
  • are they on desktop, mobile, or watch?
  • do they trust the product already?
  • is the action reversible?
  • what happens if the connection is slow?
  • what would become annoying after the tenth use?

These questions usually reveal more than a long feature discussion.

They also stop me from building the wrong interface.

For example, Crowra could have been a normal SEO dashboard. Login, add project, paste URL, run scan, open report.

That would be a valid product.

But it was not the workflow I wanted to solve.

The moment I cared about was this: the user is already looking at a page and wants to inspect it without leaving the page.

That is why Crowra became a Chrome side-panel tool.

The side panel is not just a UI choice. It matches the moment. The page stays open. The audit stays beside it. The user can scroll, inspect, fix, reload, and scan again without switching context.

That is the kind of product decision I care about.

Not “what can we add?”

More often:

Where should this live so the workflow feels natural?

Real products fail in boring ways

A feature demo usually happens in perfect conditions.

The API responds. The network works. The data is clean. The user follows the expected path. The browser is modern. The device is fast. Nothing weird happens.

Real products are not used like that.

People lose connection. They refresh at the wrong moment. They double-click. They paste strange data. They leave and come back later. They use older devices. They misunderstand labels. They expect the product to recover anyway.

This is why I try to think about failure states early.

Not because every product needs complicated architecture from day one.

Because failure is part of the user experience.

If a save action silently fails, the user does not think:

The backend request returned an error.

They think:

I cannot trust this product.

That is a much bigger problem.

So when I build a flow, I want to know:

  • what happens while the action is loading?
  • what happens if it fails?
  • can the user retry safely?
  • can the same action happen twice?
  • is the local state still correct?
  • does the UI explain what happened?
  • do we lose data?
  • do we need optimistic updates?
  • do we need sync or queueing?

A lot of product quality lives in these boring details.

They rarely look impressive in screenshots.

But users feel them.

Performance is part of the product

I do not think about performance as a final optimization step.

Performance is product work.

A slow interface feels uncertain. A delayed click feels broken. A page that jumps while loading feels unfinished. A mobile screen that needs too much JavaScript can make even a good product feel heavy.

This does not mean every project needs extreme optimization from the first commit.

It means performance should influence the shape of the product before it becomes hard to change.

I care about things like:

  • what should render first
  • what can wait
  • what should be server-rendered
  • what really needs client-side JavaScript
  • whether an animation improves the experience or only adds weight
  • whether images are sized correctly
  • whether loading states reduce confusion
  • whether the product still feels good on mobile

I wrote more about this in What actually makes a frontend feel fast in 2026.

For me, the important point is simple:

Users do not experience the tech stack. They experience waiting.

That is why performance belongs in the same conversation as UX.

I care about the system behind the screen

Screens are easy to see.

Systems are easier to ignore.

But many product problems come from the space between screens: unclear data ownership, fragile state, inconsistent validation, weak error handling, messy sync, or backend models that do not match the product.

A screen can look clean while the system behind it is already becoming painful.

That is why I like thinking full-stack.

Not because every developer has to do everything.

But because product behavior usually crosses boundaries.

A “save” button is not only a button. It involves UI feedback, local state, validation, persistence, failure handling, retry behavior, accessibility, and sometimes analytics or sync.

A “publish” action is not only an endpoint. It changes user expectations, metadata, crawlability, previews, cache, and rollback strategy.

A “dashboard” is not only charts. It has to answer a question someone actually has.

Good product engineering means understanding which of these layers matter for the current problem and not pretending the rest does not exist.

It also means not making everything complicated.

Some features should stay simple.

But the simplicity should be intentional, not accidental.

Clarity beats cleverness

I like simple products.

But simple does not mean empty.

Simple means the important thing is easy to understand.

This applies to the interface, but also to the code.

Clear naming matters. Clear data models matter. Clear component boundaries matter. Clear empty states matter. Clear error messages matter. Clear metadata matters.

I have learned to be suspicious of cleverness when it appears too early.

Clever abstractions. Clever animations. Clever navigation. Clever dashboards. Clever settings. Clever architecture that takes longer to understand than the problem itself.

Sometimes cleverness is useful.

But most of the time, products get better when they become easier to reason about.

That is why I prefer:

  • obvious flows
  • boring but stable patterns
  • clear user actions
  • predictable state
  • readable code
  • fewer hidden assumptions

These choices do not always look exciting.

They make the product easier to improve later.

UX does not end in Figma

A design can look great in Figma and still become weak in production.

The opposite is also true. A modest design can feel excellent if the product is fast, clear, accessible, and reliable.

A lot of UX is engineering.

It is focus behavior. Loading states. Error recovery. Mobile layout. Keyboard navigation. Form validation. Image loading. Metadata. Empty states. Response time. Scroll behavior. State preservation.

These details decide whether the product feels polished or fragile.

That is why I like being close to product decisions.

A developer can see problems that are not always visible in static designs:

  • this flow needs a better state model
  • this page will be too heavy on mobile
  • this form will create messy data
  • this empty state needs a real action
  • this feature needs a retry path
  • this content needs better structure for search
  • this interaction will be annoying after repeated use

That does not mean developers should block product ideas.

It means engineering should make the product sharper, not only “implemented.”

Production-ready does not mean perfect

I use the phrase “production-ready” a lot, but I do not mean perfect.

Perfect products do not ship.

For me, production-ready means the product is honest enough for real usage.

It has the core behavior. It handles the most important states. It does not rely only on the happy path. It is understandable enough to maintain. It is fast enough to feel trustworthy. It does not hide obvious broken parts behind nice UI.

Depending on the project, production-ready might include:

  • clear user value
  • predictable behavior
  • useful loading states
  • understandable errors
  • basic accessibility
  • stable data flow
  • reasonable performance
  • safe failure handling
  • correct metadata
  • maintainable implementation
  • enough analytics or logging to learn from usage

That list changes by product.

A Chrome extension, a mobile app, a backend API, and a marketing site do not need the same checklist.

But they all need the same mindset:

Do not ship something that only works when everything goes right.

The ideal path is a demo.

The real path is the product.

An example from Pean

A simple way to explain the difference is to take one product idea:

Save important places.

Feature-first thinking might lead to:

  • a map
  • a save button
  • a form
  • a database record
  • a list of saved places

That is a reasonable start.

But it is not enough to make the product feel right.

For Pean, the better questions were:

  • what if the user is outside with poor signal?
  • what if they only have a few seconds?
  • what if they want the place private by default?
  • what if they are using Apple Watch?
  • what if they want to add details later?
  • what if syncing happens after they reconnect?
  • what gives them confidence that the place was saved?

Those questions pushed the product toward one-tap capture, offline support, automatic sync, privacy by default, and simple organization after the place is already saved.

The feature is still “save a place.”

But the product becomes something more useful:

Save the spot before the moment is gone.

That is the difference I care about.

From isolated feature to complete product behavior

What this changes for teams

When developers think this way, teams get more than implementation.

They get better questions earlier.

That can prevent a lot of rework.

A product-minded developer can notice when a feature solves the wrong problem, when a flow is too heavy, when the data model does not match the interface, when a page will be hard for search engines to understand, or when performance will become a UX issue later.

This is not about turning developers into product managers.

It is about making engineering part of product quality.

The best work usually happens when product, design, and engineering are not separate handoff stations.

They should still have clear responsibilities.

But the thinking has to overlap.

A developer does not need to own every product decision.

A good developer should understand the product well enough to protect it.

What I try not to build

I try to avoid adding things only because they are easy to add.

More pages do not always make a site better. More settings do not always make a product more flexible. More filters do not always improve discovery. More animations do not always improve feel. More dashboards do not always create more clarity.

Sometimes a product needs more.

Often it needs less.

Less friction. Less confusion. Less hidden state. Less repeated work. Less unclear choice. Less UI that exists only because the data exists.

I also try to avoid features where success is undefined.

If nobody can explain what should get better after the feature ships, the feature probably needs more thought.

It does not always need a complex metric.

But it needs a reason.

A good feature should reduce friction, increase trust, unlock a workflow, improve speed, clarify a decision, support growth, or make the product easier to use.

If it only makes the product bigger, I am not convinced.

The kind of work I enjoy most

I enjoy work where product thinking and engineering quality meet.

That usually means:

  • web apps with real workflows
  • mobile experiences that need to feel fast and clear
  • browser extensions with tight interaction design
  • backend systems that support user-facing reliability
  • SEO and content surfaces where structure matters
  • product redesigns where positioning, UX, and implementation need to align

I like the middle space between idea and production.

Not only the first mockup.

Not only the final code.

The part where the product becomes real: constraints, flows, trade-offs, edge cases, performance, content, metadata, systems, and the small decisions that decide whether people can actually use it.

That is where I think good developers can create a lot of value.

My product-building loop

If I reduce my approach to a simple loop, it looks like this:

  1. Understand the real usage moment.
  2. Define the behavior, not only the feature.
  3. Map the important states: empty, loading, success, failure, retry.
  4. Build the smallest useful version.
  5. Keep the system behind the screen understandable.
  6. Make the interface fast and clear.
  7. Treat performance and accessibility as product quality.
  8. Ship with enough structure to learn from usage.
  9. Remove complexity that does not support the product.
  10. Improve based on what the product teaches you.

The hard part is not understanding this list.

The hard part is staying disciplined when it is faster to just build the ticket.

What building products has taught me

Building products has made me less impressed by long feature lists.

A product can have many features and still feel weak.

A smaller product can feel strong if it understands its moment well.

That lesson keeps repeating.

A private place-saving app does not need to become a general map. It needs to save meaningful places quickly, privately, and reliably.

An SEO Chrome extension does not need to become a full platform first. It needs to help review the page in front of the user.

A developer website does not need to list every technology. It needs to explain what kind of value the developer brings and show proof clearly.

The product gets better when the purpose gets sharper.

That is how I want to build.

Not just more features.

Better products.

FAQ

What is a product-minded developer?

A product-minded developer is a developer who thinks beyond implementation and cares about how the product works in real usage. That includes UX, performance, failure states, data flow, accessibility, maintainability, and whether the feature actually helps users.

What is the difference between building features and building products?

Building features means implementing specific pieces of functionality. Building products means designing complete behavior around real user needs, constraints, edge cases, and long-term product quality.

Why should developers think about product?

Developers make many decisions that directly affect product quality: performance, state management, data modeling, error handling, accessibility, technical trade-offs, and maintainability. Product thinking helps those decisions support user value instead of only completing tasks.

Does product-minded development slow teams down?

It can make the first conversation a little deeper, but it usually reduces rework. Clear behavior, better failure handling, and stronger system boundaries make products easier to ship, maintain, and improve.

Is performance part of product quality?

Yes. Performance affects trust, clarity, and perceived quality. A slow interface can make a useful feature feel broken, especially on mobile or in repeated workflows.

How do I become a more product-minded developer?

Start by asking better questions before building: who uses this, when do they use it, what can fail, what should happen next, what does success look like, and how will this decision affect future versions of the product?


Related reading:

Share this post

Send it to someone who might find it useful.