Nabeel HassanMost of the AI work that lands on my desk now is not a new AI product. It is an existing product,...
Most of the AI work that lands on my desk now is not a new AI product. It is an existing product, with paying customers and years of schema behind it, whose owner has been told to "add AI" before a competitor does. Sometimes the push comes from the board, sometimes from sales, sometimes from one customer on a renewal call.
The first draft is almost always the same: a chat assistant in the bottom right corner that answers questions about the product. It demos beautifully. Then usage falls off a cliff within a few weeks, because nobody opened the product to have a conversation. They opened it to get a piece of work done.
As an engineer, I find the interesting part is that the model is the easiest piece of the whole thing. The work is in the boring layers around it: which data goes in, whose permissions it runs under, where the output lands on screen, and what happens when the model is wrong or down. That is what this post is about.
"Where can we use AI?" is the wrong question. The useful one is: which task do users already do in this product, over and over, that is slow, tedious or error-prone?
The candidates that keep working share a shape:
Every one of those has an existing workflow you can measure today. That gives you a baseline, and a baseline is the only way you will ever know whether the feature did anything.
My own app, LectureNotes AI, is a clean example even though AI was in it from day one. Nobody records a lecture because they want to chat with a model. They want notes they can study from. The transcript, the key takeaways and the structured outline all serve that one job, and none of them is a chat window. (I wrote about what that app taught me separately.)
Mature products were built to show data to humans, not to hand it to a model. That gap is where most of the build time goes.
Assembling context is ordinary engineering, and it is most of the job. For "summarize this customer's history", somebody has to decide which records count as history, how far back to go, which fields matter and which to leave out. In a product that has been around a few years, that data lives across several tables, a couple of services and at least one third-party tool. Retrieval quality decides answer quality far more than model choice does, which is the same thing I ran into on RAG projects.
The AI feature must obey the permissions you already have. This is the one I would underline. If a user cannot open a record, the assistant must not summarize it, quote from it, or let it leak into a search answer. The tempting shortcut is to give the AI feature its own service account with broad read access, because it is faster to build. Do not. Fetch context as the requesting user, through the same access layer the rest of the product uses.
Know where the data is allowed to go. If your customers signed contracts with data-handling terms, sending their records to a model provider may or may not be covered. Find out before the build. If it is not covered, there are workable options: zero-retention provider terms, a specific processing region, a self-hosted model, or stripping sensitive fields before the call.
Here is roughly what the "same access layer, fail quietly" shape looks like. This is illustrative, not lifted from a client codebase:
// Illustrative: the AI panel reuses the product's own access layer
// and never blocks the page it lives on.
async function getRecordSummary(user: User, recordId: string) {
// Same call the record page already makes. If the user can't see it,
// this throws exactly like it would for the normal UI.
const record = await records.getForUser(user, recordId);
const history = await activity.listForUser(user, recordId, { limit: 50 });
const context = buildContext(record, history); // only the fields the task needs
try {
return await withTimeout(llm.summarize(context), 4000);
} catch (err) {
log.warn("summary_unavailable", { recordId, err });
return null; // page renders without the panel
}
}
Two things matter in there. There is no privileged path to the database, so a permission bug in the AI feature is impossible by construction rather than prevented by review. And a slow or failing provider returns null instead of hanging the screen. The product worked before the feature existed. It should keep working when the feature cannot.
Placement is the single biggest factor in whether anyone keeps using an AI feature. The pattern that works is to put the output inside the screen the user is already on, at the moment they need it:
In each case the existing workflow stays intact and the user stays in control. If the AI is wrong, they fix it the same way they would fix their own mistake. If it is right, they saved time without learning a single new thing.
A general chat assistant can earn a place later, once you know which questions people actually ask. It is almost never the right first feature.
Every AI feature will sometimes produce a bad output, so treat that as a normal code path, not an edge case.
Normal features cost about the same whether a customer uses them once a month or all day. AI features do not. Every summary and every draft is a metered call, and your heaviest users can cost many times what your lightest ones do.
Do the arithmetic at realistic usage per active user per month before launch. If the number hurts, the fixes are architectural: generate the summary once when the record changes instead of on every page view, send only the context the task needs, and route narrow tasks to smaller models. I wrote a whole post on building AI features as if calls were metered, because they are.
It also pays to route every model call through one internal interface with prompts in configuration. Then trying a cheaper or better model is a config change plus an eval run, not a rewrite.
Pick one slow, repeated job. Feed the model clean context fetched under the user's own permissions. Put the output exactly where users already work, make it fail quietly, and measure whether it sticks. One feature people keep beats five they tried once.
This is adapted from a longer guide on the Null Studio blog, where we cover the commissioning side too. I am curious what others have seen: has anyone shipped the bottom-corner chat assistant and had it actually stick? What made the difference?