AppliedAIPrep logoAppliedAI/Prep
Courses/Applied AI Engineering/What this job actually is15 min read

Five habits from normal software that will hurt you here

The hardest part of moving into applied AI is not the new material, it is the instincts you already have. Five of them are actively wrong here. This lesson names each one, what replaces it, and why the substitution is not optional.

TL;DR: Your code either works or throws. A model always returns something, and the wrong answer arrives with the same confident tone as the right one, like a colleague you have not yet learned to distrust. That single difference invalidates five habits you have spent years building: testing by assertion, trusting a passing run, treating compute as free, treating the prompt as configuration, and shipping when the demo works. Each has a replacement, and learning the replacements is most of what makes someone effective here.

Where you are. You know the four shapes of the work. All four require the same shift in engineering instinct, and that shift decides whether your first project goes well. Everything here assumes you have shipped software before; that experience is an advantage, apart from these five specific places where it points the wrong way.

Why one difference causes all five

In the software you have written, a function has a contract. Give it valid input and it returns a defined output or raises. When something is wrong you get a stack trace, a failing assertion, a 500. The system tells you.

A model call has no such contract. It returns fluent, well-formed text every single time, including when the content is nonsense. There is no exception for "this answer is wrong." Correctness is a property of the content, and nothing in the machinery is checking it.

Everything below follows from that.

rendering diagram…

Habit 1: testing by assertion

The habit. Write a test, assert the exact output, green means correct.

Why it breaks. Ask for a summary and you get a good one. Ask again and you get a different good one. Assert on the string and you have a test that fails on correct behaviour, which is worse than no test, because the team learns to ignore it.

The replacement. Assert on properties rather than strings. Is it valid JSON with the required fields. Is every claim traceable to a supplied source. Is it under the length limit. Does it refuse the thing it should refuse. Then, for the parts that are genuinely a matter of quality, score a set of examples and watch the score over time rather than demanding perfection on each.

This is called evaluation, it is module 6, and it is the single biggest skill gap between people who demo and people who ship.

Habit 2: trusting a passing run

The habit. It worked when I ran it, so it works.

Why it breaks. One run samples one output from a distribution. The same input can produce a different result next time, and the failure rate you care about is often one in twenty, exactly the rate a handful of manual tries will miss.

The replacement. Run it enough times to see the spread, and treat behaviour as a rate rather than a state. "It works" should mean "it produced acceptable output on 47 of 50 examples, and here are the three that failed." A single successful run tells you the code path executes, which is worth knowing and is not the same claim.

Habit 3: treating compute as free

The habit. Loops are cheap, so call it again, retry on anything, add a second pass for safety.

Why it breaks. Every call costs real money and real seconds, and both scale with how much text you send. A retry loop that would be invisible around a database query is a doubled bill around a model call. An innocuous "just also check it with a second call" doubles the cost of every request in production forever.

The replacement. Carry a rough cost and latency figure per request the way you would carry a memory budget on an embedded system. Do the arithmetic before you build, as you did in the tokens lesson. When you add a call, say what it costs.

Habit 4: treating the prompt as configuration

The habit. The prompt is a string, so it lives in a constant or an environment variable, and changing it is a config tweak.

Why it breaks. The prompt is the program. It is the largest single determinant of behaviour, and a two-word change to it can alter output across every request. Editing it without review, without version history, and without measuring the effect is shipping unreviewed code straight to production.

The replacement. Treat prompts as source. In version control, reviewed, and changed only with a before-and-after measurement on a fixed set of examples. If you cannot say what a prompt change did to your numbers, you do not know whether you improved anything.

Habit 5: shipping when the demo works

The habit. The happy path works, the edge cases are a backlog item.

Why it breaks. For deterministic software this is a defensible trade, because the failure modes are enumerable and mostly discovered by users hitting them loudly. Here the failure mode is a confident wrong answer, which is silent. Nobody files a bug for an answer that sounded reasonable and was wrong. You will not find out from your users; you will find out from the consequences.

The replacement. Before shipping, know what the system does when it is unsure, when retrieval finds nothing, when the model returns something unparseable, and when the provider is slow or down. A system that says "I could not find that" is more valuable than one that is right slightly more often and confidently wrong the rest of the time.

The one that transfers completely

None of this means your experience is a disadvantage. The opposite: nearly everything that makes someone a good engineer transfers directly, and the parts of these systems that fail hardest in production are ordinary distributed-systems problems wearing a new hat. Timeouts, retries with backoff, idempotency, graceful degradation, observability, cost control. People arriving with strong AI knowledge and weak engineering habits struggle more than the reverse.

What changes is the shape of correctness. Not your standards for it.

Do this before moving on

Take a service you have built and write down how you know it is working: the tests, the alerts, the dashboards.

Now go through that list and mark each one that would still function if the component returned fluent, plausible, wrong output instead of throwing. In most lists, nearly nothing survives. The items that do not survive are the ones you would have to replace, and the replacements are what modules 6 and 7 are about.

Go deeper

Key takeaways

  • A model always returns fluent output, including when it is wrong. There is no exception for an incorrect answer, so nothing tells you but a check you built.
  • Assert on properties and score a set, rather than asserting on strings or trusting one run.
  • Cost and latency per request are a budget you carry, not an afterthought. Every extra call is permanent spend.
  • The prompt is the program: version it, review it, and measure the effect of changing it.
LEARNING LAB1 of 4

Check yourself before an interviewer does. Answer from memory first.

A test asserts that a summarisation endpoint returns an exact expected string. It fails intermittently on output a human would call correct. What is the fix?

Sign in to track where you are in this course.