Build and deploy a working AI service that exercises all four fields at once — then break it on purpose, because the failure path is what gets you hired.
By the end of this build you will have a working AI service, deployed at a public URL, that a stranger can use without you present. That artifact is your portfolio. Not a certificate — the URL.
It is deliberately one project rather than four, because one system exercises all four fields at once:
| Cloud | it is deployed, it has an endpoint, it holds state, it handles load and failure |
| AI | it calls a language model and shapes its behaviour |
| Machine learning | it classifies and routes before it spends money on the model |
| Robotics | it is a sense–think–act loop; swap the input for a sensor and the output for a motor and nothing else changes |
That last row is the point. People treat robotics as a separate world because it has hardware. It is the same loop with a different input and a different output.
You will build on PHP on shared cPanel hosting, with Supabase for data. Some people will tell you that is not a real engineer's stack. Consider what actually matters:
It costs a few thousand naira a month, not a credit card you cannot afford when the free tier ends.
It is deployed today, not after two weeks of configuration.
The six patterns are identical on every stack. An interviewer asking how you made a webhook safe to retry does not care whether it ran on Lambda or on cPanel.
Learn the patterns where it is cheap. Move them to bigger infrastructure when someone else is paying for it — which is a two-day job once the patterns are in your hands, not a two-month one.
Read this before writing a line. Every box below exists because of a pattern from the previous lesson.
Step 3 is the one that separates you from most people building on top of AI models. Almost everybody sends every request straight to the model and is surprised by the bill. A classifier that costs nothing, filtering before a call that costs money, is a real engineering decision — and it is the sort of thing you say in an interview.
Do not paste all of this at once. Build one step, run it, break it deliberately, then move on. Code you have watched fail is code you understand.
<?php
// ask.php — the loop: take in, decide, act
header('Content-Type: application/json');
$input = json_decode(file_get_contents('php://input'), true);
$question = trim($input['question'] ?? '');
if ($question === '') {
http_response_code(400);
echo json_encode(['ok' => false, 'error' => 'Ask me something.']);
exit;
}
// everything else goes here
echo json_encode(['ok' => true, 'answer' => 'not built yet']);
Break it on purpose: send an empty body. Send malformed JSON. Send a 50,000-character question. Whatever it does badly now is what it will do in production.
$key = 'ans:' . hash('sha256', mb_strtolower($question));
$cached = cacheGet($key); // your Supabase read
if ($cached) {
echo json_encode(['ok' => true, 'answer' => $cached, 'cached' => true]);
exit; // no model call, no money spent
}
Measure it: time the same question twice. Write down both numbers. That difference is the Two Clocks pattern in milliseconds, and it is a sentence in your interview.
// A cheap local classifier. No model call, no cost.
// Later you replace these keywords with a trained classifier —
// the SHAPE of the code does not change, only what decides.
function inScope($q) {
$topics = ['loop','cloud','robot','model','deploy','api','data','train'];
$q = mb_strtolower($q);
foreach ($topics as $t) if (strpos($q, $t) !== false) return true;
return false;
}
if (!inScope($question)) {
echo json_encode(['ok' => true, 'answer' => 'I only answer questions about building systems.']);
exit;
}
Notice what you just did. You made an engineering decision about cost, not just a
feature. When you later swap those keywords for a trained model, steps 1, 2, 4, 5 and 6 do not change
at all. That is Pattern 4 — you hid the method behind a promise called inScope().
$ch = curl_init('https://api.anthropic.com/v1/messages');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_TIMEOUT => 20, // ALWAYS set this
CURLOPT_HTTPHEADER => [
'x-api-key: ' . kodiConfig('ANTHROPIC_API_KEY'), // never in JS
'anthropic-version: 2023-06-01',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'model' => 'claude-haiku-4-5-20251001',
'max_tokens' => 400,
'system' => 'You explain system-building to a 16-year-old in plain words. '
. 'If you are not sure, say so. Never invent an API that does not exist.',
'messages' => [['role' => 'user', 'content' => $question]],
]),
]);
$raw = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
Two things most people get wrong here. No timeout means one slow call holds a connection open until your whole site stops answering. And the key belongs on the server — the moment it is in JavaScript, it is public and someone else is spending your money.
if ($code !== 200 || !$raw) {
logEvent('model_failed', ['code' => $code]); // LOUDLY
echo json_encode([ // SOFTLY
'ok' => true,
'answer' => 'I cannot reach my brain right now. Try again shortly.',
'degraded' => true,
]);
exit;
}
Test this properly: put a wrong API key in on purpose and call your endpoint. A user must see a calm sentence. You must see a log line. If you see a stack trace on screen, you have failed both halves.
This step is the single most valuable thing in the whole build. Nearly every portfolio project you will compete against has no step 5 at all.
cacheSet($key, $answer);
logEvent('answered', [
'q_hash' => substr($key, 4, 12),
'ms' => $elapsedMs,
'cached' => false,
]);
echo json_encode(['ok' => true, 'answer' => $answer]);
Log the hash, not the question. You do not need people's raw text sitting in a table, and choosing not to collect it is a decision you should be able to defend out loud.
// /api/feedback.php — thumbs up or down against the question hash
// Once a week, read every thumbs-down. Those are your eval set.
// Adjust the system prompt. Re-run them. Did the bad ones improve?
// Did any GOOD ones get worse? <-- that second question is the job.
This is gradient descent done by hand: measure the error, make a small correction, measure again. Same pattern as PID, same pattern as training. You are the optimiser.
Deploy to a real domain. A localhost screenshot is worth nothing to an employer.
Put a rate limit on it before you announce it. One person with a script can spend your whole balance overnight.
Give ten strangers the link and watch what they type. They will break it in ways you never imagined, which is exactly why you need them.
Write a README with an architecture diagram and one paragraph per pattern explaining why each piece exists.
Record a 2-minute screen video: here is the happy path, here is what happens when I break the model call. That video gets you interviews.
When your service works, change two things and nothing else:
Instead of a typed question, a sensor reading arriving from an ESP32 over wifi.
Instead of returning text, return a command that turns a motor.
Steps 2, 5, 6 and 7 do not change at all. You still cache, still guard, still log, still correct.
You have not learned a new field. You have swapped the two ends of a loop you already own — and that realisation, honestly earned, is worth more than any certificate on the wall.
The Practitioner track is built for adults. It assumes you are ready to deploy real systems that real strangers will use, and to be answerable for what they do.
We ask because in Nigeria you become an adult at 18, and this track is a paid commitment. Nothing here is stored for anyone under that age.