Pitfalls of Database Migrations

Oct 3, 2023

Database migrations sound difficult but are even more difficult in practice. Unlike stateless code, they are an arrow in time.

  1. Development / production parity is impossible. You can (and should) try to recreate production state as much as possible. This is much easier at the API and web tiers of your application. There might be different endpoints, different API keys, or different network rules, but there are workarounds for coercing them to be somewhat reproducible between environments. When it comes to databases, all bets are off. Development environments won’t have live data (and shouldn’t have personally identifiable information).
  2. Rollbacks are great in theory but tough in practice. Having a “down” migration is usually seen as a major feature of most frameworks but doesn’t work in practice. Each “down” migration requires special care — what do you do with the existing data? Not all transformations have an inverse. A sophisticated “down” migration is just another forward migration.
  3. Application and database migrations can’t happen atomically. It’s hard to orchestrate dual changes between APIs and the database schemas they rely on. Changes in development that work will fail in production (maybe intermittent downtime). What happens when you need to support backward compatibility (for either app or database)?
  4. Schema state is hard to version control. After many migrations, how can reliably recreate the current database state? In a perfect world, it’s just running all of the existing migrations. But in practice, long-lived environments often accumulate undocumented or unintentional changes. In the application world, this is easy with ephemeral services and environments that are often short-lived.
Daily posts on startups, engineering, and AI

Generative Interfaces

Oct 2, 2023

The “wizard” interface design has been around for decades: interfaces that guide users step by step through complicated procedures, usually installations. Even today, you see them on the web with software like Typeform (splits form questions into a multi-step UI, which is aesthetically pleasing and maybe even increases conversion).

One idea I’ve been thinking about recently is generative interfaces. These interfaces have to be designed individually for every task. There are “no-code” builders that help design these wizards, but they still require work. The logic gets complicated exponentially fast — every branch in the decision tree can add many different wizard states.

What if we could compile designs just in time with generative AI? Given the current application state, the UI is conditionally rendered according to the output of some AI. For a form, this might be letting the user input multiple answers in a free-form text box, have the AI try to parse it into the structured output, and then ask clarifying questions for the remaining or unclear values.

For application onboarding, it might allow users to have a customized journey — what are you interested in learning about? How will you use the application? How familiar are you with the application already?

Instead of dealing with a raw chat interface — there might be richer elements — input boxes, sliders, select forms, or other interactive elements.

One more thought — generative interfaces might be best served as a “design in the small” paradigm. That is, instead of trying to generate an entire application, they might be more useful if they generate a single element or piece of the UI. Maybe a foundation of generative UI components that you can assemble together.