Design From the Outside In

Most software developers are used to starting somewhere near the inside.

We think about the data we're going to need. We think about fields. We think about features. We think about screens. We think about what the program might need to know and all the things somebody might eventually want it to do.

Then we start building.

Eventually, if everything goes according to plan, something useful comes out the other end.

I've been developing software for more than 40 years, so I'm certainly familiar with that process. It's a very natural way for a programmer to think.

I'm becoming convinced that it's often the wrong way to design a product.

AI makes that problem even more important.

AI can build almost anything you can think of

One of the great things about AI-assisted development is that implementation has become incredibly cheap.

Need another field?

No problem.

Need another setting?

Easy.

Need another screen to manage the setting?

AI can build that too.

Need another option because somebody might want to work a different way someday?

Give it a few minutes.

That's powerful.

It's also dangerous.

When software was expensive to write, the cost of implementation provided a natural brake. Somebody had to design the feature, write the code, debug it, test it, document it, maintain it, and support it.

That didn't prevent feature creep, but it made us think about the cost.

AI removes a great deal of that friction.

Unfortunately, it doesn't remove the complexity created by what we build.

AI is also exceptionally good at taking an open-ended product description and filling in the blanks with things that sound reasonable.

Programmers are pretty good at that too.

That's how we wind up with products containing fields nobody uses, options nobody understands, settings nobody asked for, and workflows designed around everything the program can do instead of what the customer came there to accomplish.

We were heading in that direction

We're currently developing a new customer-facing product.

We began the way most software projects begin.

We had an idea.

We worked through what the product needed to do.

We developed what I still think was a pretty good specification.

Then we started building a proof of concept.

It worked.

That was almost part of the problem.

There wasn't some dramatic failure forcing us to reconsider the project. The application functioned. It didn't look terrible. Most of the individual decisions were reasonable.

But the more I looked at it, the more I realized we were beginning to build the kind of application programmers and AI naturally build when they're allowed to make local decisions as they go.

It looked assembled.

The controls worked, but there wasn't a strong enough visual language holding everything together. One feature suggested another feature. One piece of data suggested another field. The application was beginning to grow according to what we could build instead of according to the story the product needed to tell.

So we stopped.

First, constrain the building materials

One of the first decisions was fairly straightforward.

We standardized the user interface around a framework and control system we were already using elsewhere.

That matters more than it might seem.

If you don't give AI a design system, it will happily solve each new interface problem independently.

One screen may use one kind of control. Another may solve almost the same problem differently. Every individual result can look perfectly reasonable while the application as a whole feels like it was assembled from unrelated parts.

Choosing a framework and a standard control set puts a fence around that behavior.

There is now a visual language.

There are established controls.

There are established interaction patterns.

AI doesn't get to invent a new universe every time it creates a screen.

But that only solved part of the problem.

We still had to decide what we were actually building.

Start with the promise

That's when we turned the process around.

Instead of starting with the application, we started with the place where the product would eventually be introduced and sold.

What are we promising?

What does somebody need to understand before they can say:

I get it. I want one of those.

Not what technology are we using.

Not what database sits behind it.

Not what clever things could we make it do.

What does this product do for the person buying it?

What problem does it solve?

What are they actually getting?

That forced us to get the story straight before we built more software.

Once you know the promise, you have something against which every later decision can be measured.

Does this help fulfill the promise?

If it doesn't, why are we doing it?

Then design the finished thing

From there, we did something that feels backward from the way programmers normally work.

We designed the final output.

Not a placeholder.

Not a programmer mockup showing approximately where the information might go.

We worked through what we actually wanted the finished customer-facing result to look like.

What information belongs there?

How much is too much?

What is the visual hierarchy?

What needs to be obvious?

What can disappear?

What should the experience feel like?

We kept working until the result looked like something we would actually want to sell.

We didn't need a database design to do that.

We were designing the thing the customer was buying.

Then we did the same thing for the other ways that result would be presented.

What should somebody see?

Which information matters at that moment?

How should it be presented on a smaller screen?

Should the desktop experience simply be a wider version of the same thing, or does it deserve an experience designed for the desktop?

Again, we weren't designing database fields.

We were designing outcomes.

Then design the path to the outcome

Only after we knew what those outputs looked like did we turn around and ask what information the customer needed to provide.

That's a very different question from asking what information the system might someday find useful.

We weren't designing a generic editor.

We had blanks to fill.

The finished result needs this.

Another part of the experience needs that.

Therefore the creator needs to supply those things.

Now the question becomes:

What's the shortest, cleanest path that lets them give us what we actually need?

Suddenly there isn't much room for feature creep.

If the final output doesn't require 27 fields, we don't need to ask the customer to fill in 27 fields.

If an option doesn't improve the result, it has to earn its way into the product.

If we can infer something instead of asking the user, maybe we should.

The output has become part of the specification.

The output becomes the specification

I think that's one of the most important lessons I've taken from this project.

Normally, we write a specification describing what the software is eventually supposed to produce.

With this approach, the finished result itself becomes a large part of the specification.

We know what the customer-facing product needs to contain.

We know what the different presentation experiences need to contain.

We know what promise was made before the customer ever entered the application.

Those things now constrain everything behind them.

Somebody proposes another field?

Fine.

Where does it show up?

Somebody proposes another option?

What part of the customer's experience does it improve?

Somebody proposes another feature?

Which part of the product's promise requires it?

Those are much better questions than:

Could somebody possibly find this useful?

Almost anything could possibly be useful.

That's how software gets fat.

The sequence is almost completely reversed

The pattern we're following looks something like this:

Promise → Output → Experience → Inputs → Data → Implementation

Compare that with the way software often grows:

Data → Features → Screens → More features → Output → Eventually figure out how to sell it

That second sequence isn't always wrong.

There are systems where the underlying data or technical capability really is the product.

But for a customer-facing product, particularly one where simplicity and presentation matter, starting from the inside gives implementation decisions an enormous amount of influence over product design.

Starting from the outside puts the customer experience in charge.

Now the database has to serve the product.

The product doesn't have to expose the database.

Edge cases don't get to design the product

This doesn't mean edge cases don't matter.

Eventually they matter a great deal.

We still have to think about unusual input, persistence, failed transactions, browser behavior, accessibility, output variations, data integrity, security, and all the other things required to turn a clean design into dependable software.

But those things don't get to define the center of the product.

First establish what the normal experience is supposed to be.

Then make it resilient.

Otherwise every hypothetical situation becomes another field, setting, mode, warning, dialog, or configuration option.

Before long, the exceptions have taken over the application.

Handle the edge cases after you know where the center is.

Version 1.0 can look like a product

The difference we're seeing in this project is striking.

The product doesn't have the usual proof-of-concept feeling.

It doesn't look like we put enough controls on a screen to make something run and plan to clean it up later.

It doesn't look like a collection of AI-generated pieces because we're not asking AI to invent the product one assignment at a time.

The product is being designed first.

AI is helping implement that design.

That's an important distinction.

We're also not spending our time removing things we never needed.

There isn't a pile of speculative functionality waiting to be trimmed away because most of it never got built.

We're filling in the layers behind a known result.

That has another benefit.

The closer we get to being able to run the product end to end, the less it feels like we're approaching an early prototype that will need a major design pass later.

The design pass happened first.

The implementation is catching up with it.

AI makes this more important, not less

AI doesn't eliminate the need for product judgment.

It increases the value of it.

When building something becomes easier, deciding what deserves to be built becomes more important.

AI can create another field in seconds.

It can't decide whether that field belongs in the story you're trying to tell unless somebody has first defined the story.

AI can build another workflow.

It can't decide whether that workflow helps the customer or merely demonstrates another thing the software is capable of doing.

AI can give you ten reasonable ways to solve a problem.

Somebody still has to decide whether the problem belongs in the product at all.

That's our job.

The interesting thing is that once those decisions have been made, AI becomes dramatically more useful.

Instead of asking it to wander around an open design space inventing possibilities, we're giving it a constrained problem.

Here's the promise.

Here's the finished result.

Here's the experience.

Here's the visual language.

Now help us build the machinery necessary to make those things real.

That's a very different relationship with AI.

And so far, it's producing a very different kind of software.


Related field note

For the project experience that turned one simple question into a surprisingly useful design test, read Where Does That Field Show Up?.