Nuno Bispo (aka Developer Service) (@DevAsService) header
Nuno Bispo (aka Developer Service) (@DevAsService) profile picture

Solutions Architect · Senior Python & AI Engineer · AI Audits · Helping teams fix what they shipped too fast

Netherlands
Joined July 2021
I'm Nuno Bispo, a Senior Software Engineer and Solutions Architect with 15+ years building Python systems, AI integrations, and production backends. I write for Real Python and Unstract. I build and ship hardware. I've seen what breaks in production and what doesn't. I take on a small number of advisory engagements with teams that are serious about getting their Python and AI stack right.
4
342
An AI agent looks like a chat interface, but operationally it behaves like a distributed system. It depends on a remote service you don’t control. It has variable latency and occasional timeouts. It retries on failure, sometimes in ways that multiply cost. And it can return something that looks like success while being subtly wrong. Engineers already know how to handle that: contracts at the boundaries, tests that catch regressions, budgets and alerts, staged rollouts, and a plan for when things degrade. What’s missing is applying those habits to agents, because most agent tutorials stop at the point where the model returns a response. Production AI Agents with PydanticAI picks up where those tutorials end. You build a GitHub issue triage assistant and give it the same treatment as any service you’d put on call: typed output contracts, eval suites in CI, cost and latency observability, canary rollouts, and a runbook. Free sample chapters: leanpub.com/production-ai-ag… #PydanticAI #LLMOps #AIEngineering #PythonEngineering
4
2
45
Replying to @Pulzivo
True. But it is amazing the lack on proactive monitoring in Production systems. Every monitors for error messages but noboby monitors for business failures.
7
A question I keep getting from Python developers: "Why does my function see a variable I never passed in, but crash when I try to change it?" It's a great question, because it points at exactly how Python thinks about names. Reading a variable is easy. Python searches Local, then Enclosing, then Global, then Built-in, and uses the first match. But the moment you assign to that name inside a function, Python decides it's local for the entire function. Reach for it before the assignment and you get an UnboundLocalError, even though a perfectly good global exists. Two rules, one confusing symptom. And once you've seen it explained clearly, you can't unsee it. That's the kind of thing I cover in my free Python Scope Guide: LEGB, closures, late binding, and global vs nonlocal, with examples you can run and break yourself. 🎁 Free: python-variable-scope.develo… What's the scope bug that got you? Tell me in the comments. #Python #PythonTips #SoftwareEngineering #LearnPython
1
22
A habit that saves a lot of pain in Python AI integrations is pinning your model version explicitly instead of pointing at a floating alias. Aliases like “latest” are convenient, but they mean the behaviour of your application can change on the provider’s schedule rather than yours. Outputs shift slightly, formatting drifts, a prompt that was carefully tuned starts behaving differently, and nothing in your own repository changed to explain it. The fix is to treat the model identifier as configuration you control. Pin a specific version, record it alongside every logged call, and upgrade deliberately: run your test cases against the new version, compare outputs, and only then switch. When something does change in production, you can immediately rule out the model as the cause, or confirm it, instead of guessing. It’s a small discipline, but it turns model upgrades from something that happens to you into something you schedule, test, and roll back if needed. #Python #AI #LLM #SoftwareEngineering #DevOps
1
34
The dashboard was wrong for three weeks before anyone noticed. A source system started sending revenue in cents instead of dollars. Nothing crashed. The pipeline loaded the rows, the charts updated, and the numbers looked plausible enough that nobody questioned them. Meanwhile the leadership team made a hiring decision based on them. Analytics pipelines fail quietly because they're built to be tolerant. A null becomes zero. A string that doesn't parse gets skipped. A duplicated batch doubles a total. The pipeline reports success each time, and the damage only shows up when someone compares the dashboard to the bank account. The cost goes beyond the bad decisions made in the meantime. Once people catch one wrong number, they stop trusting the others, and your data team spends weeks proving what was right instead of building anything new. Validating records as they enter the pipeline changes that. Define what a valid row looks like, quarantine the ones that don't match, and alert on the count. A unit change then shows up as a spike in rejected rows on day one, with the exact field and value attached. Practical Pydantic covers how to build that validation layer for Python data pipelines and the services that feed them. 👉 practical-pydantic.com/ #EngineeringLeadership #DataEngineering #DataQuality #TechStrategy #Python
3
3
56
Most AI features get their first real security review after something goes wrong, usually when a customer asks a question in a procurement questionnaire that nobody on the team can answer confidently. Where does user data go when it's sent to the model? Is it retained by the provider? Who on the team can see prompts and responses in the logs? These are reasonable questions, and the awkward part is that the honest answer is often "we'd have to check". That moment is expensive because it usually arrives attached to a deal or a renewal, which means the review happens under pressure, on someone else's timeline. Teams that have already mapped their data flows and access controls answer in an afternoon. Teams that haven't can lose weeks, and sometimes the contract. If you couldn't answer those questions today without a scramble, it's better to find the gaps on your own schedule than a customer's. Let me help you get that clarity => developer-service.blog/work-… #AI #CTO #TechLeadership #SoftwareEngineering #Python
1
21
There's a moment in most AI projects when someone says lets just get it working first, and well make it robust afterwards. It sounds sensible, and sometimes it is. The trouble is that afterwards almost never arrives on its own. The feature starts earning attention, the roadmap fills up, and the robust version quietly becomes a project nobody has the time or the mandate to start. What I've noticed is that the teams who do get to robust aren't the ones who found more time. They're the ones who decided, early and explicitly, which two or three things would never be deferred: secrets handled properly, failures visible, outputs validated. Everything else could be rough. Those few things couldn't. Its a much smaller promise than well make it production ready later, which is exactly why it tends to get kept. #SoftwareEngineering #AI #TechLeadership #CTO #Python
22
🚨 PHP2Shell: if you run PHP anywhere, patch today. On Oct 21, PHP shipped emergency releases for two critical flaws (CVE-2026-109618 and CVE-2026-109619). Unauthenticated remote code execution, rated CVSS 9.2 by the PHP team. The scope is wide: → PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed are all affected → Nearly every version back to 5.4 is exposed to at least one of the issues → WordPress, Drupal, Magento, Joomla and Laravel sites are exposed if the PHP underneath is vulnerable Fixed in: 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Minimal backports exist for 7.4, 8.0 and 8.1, or you can use your distro or LTS vendor’s package. Updating is the only complete fix. The published settings reduce exposure to specific attack paths, but the PHP team is clear that they don’t make an install safe. One more thing to check after patching: the patched runtime can kill a worker immediately at its hard memory limit, bypassing normal shutdown handlers. Test your app under memory pressure. “We’re a Python/AI shop, this doesn’t affect us.” Are you sure? Count the marketing WordPress site, the old admin panel, the vendor container nobody has touched in two years. The real lesson from PHP2Shell isn’t about PHP. It’s that most teams can’t answer “what are we actually running in production?” in under an hour. That’s exactly what I help with. I work with CTOs and engineering leads at AI/Python teams who ship fast and want to be sure it’s secure, observable and maintainable: 🔹 Architecture Call: a focused session on your biggest risk 🔹 AI Code Health Check: a fast review of the code your team (and your AI) is shipping 🔹 Production Readiness Audit: a full inventory of what runs and where it can break 🔹 Rescue & Stabilise Retainer: for when something is already on fire If this week made you wonder what else is running in your stack, let’s talk : 👉 developer-service.blog/work-… #PHP #CyberSecurity #DevSecOps #Python #SoftwareArchitecture
194
A one-line prompt tweak can change the behavior of an agent more than a thousand-line refactor. But in most codebases, the prompt change gets a quick glance and a thumbs up, while the refactor gets a full review and a test run. That mismatch is where a lot of agent incidents start. Someone adjusts the wording to fix one annoying edge case, it looks harmless in the diff, and three days later the output distribution has shifted in ways nobody can trace back to a specific change. The fix is to treat prompts, models, and output schemas as versioned code that has to pass the same gates as everything else: a typed contract the output must satisfy, an eval suite that runs on the pull request, and a canary stage before the change reaches everyone. Thats the approach in Production AI Agents with PydanticAI, where you build a GitHub issue triage assistant chapter by chapter and wire each of those gates into the repo as you go. Free sample chapters: leanpub.com/production-ai-ag… #PydanticAI #LLMOps #AIEngineering #PythonEngineering
3
1
1
44
If I could pin one mental model above every Python developer's desk, it would be this: Python resolves a name by searching four places, in order, and stopping at the first match. 1. Local — inside the current function 2. Enclosing — any outer functions wrapping it 3. Global — the module level 4. Built-in — names like len, print, range That's LEGB. Four layers, one direction, zero exceptions. Almost every confusing scope bug I've seen comes down to a developer assuming Python searched somewhere it didn't, or stopped somewhere they didn't expect. Shadowed names. Closures capturing a variable instead of a value. UnboundLocalError from a single assignment lower in the function. The model is simple. Applying it under pressure, in real code, is the skill. That's why I wrote a free Python Scope Guide: LEGB in depth, closures, late binding, and global vs nonlocal, with practical examples you can run yourself. 🎁 Free: python-variable-scope.develo… #Python #PythonTips #SoftwareEngineering #LearnPython
1
31
One habit that makes Python AI integrations much easier to live with is keeping a small set of real test cases that you run against every prompt or model change. Not a full evaluation framework, just fifteen or twenty representative inputs with the outputs you’d consider acceptable, including a few awkward edge cases you’ve already been bitten by. Without that, every change is judged by eyeballing two or three examples, and regressions only show up when a user finds them. The setup is simple. Store the cases in a file, run them through your actual pipeline, and compare the results against expectations using properties rather than exact string matches: does the output parse, does it contain the required fields, is the classification one of the allowed values, is the length within bounds. Add a case every time something goes wrong in production, and the suite becomes a record of everything your system has already learned the hard way. It takes an afternoon to build and changes how confident you can be in a prompt tweak or a model upgrade, which for most teams happens far more often than they planned for. Need help setting it up? Then developer-service.blog/work-… #Python #AI #Testing #LLM #SoftwareEngineering
16
When a business rule lives in fourteen places, it will be wrong in at least one. Take something as small as how your system treats an email address, a price, or a phone number. One service trims whitespace and lowercases. Another doesnt. A third rejects plus signs. Nobody chose these differences. They built up as different people copied slightly different checks into different files. Then a customer cant log in, or an invoice is off by a cent, and someone spends a day finding out which version of the rule was running. Pydantic's annotated types let you define a rule once and reuse it everywhere. You declare what a valid email or a valid money amount looks like in a single place, and every model that uses that type gets the same behavior. When the rule changes, say a new rounding policy or a stricter ID format, its one edit and one review. The change reaches every service that depends on it. For a team thats growing, that matters more than it sounds. New engineers don' t have to learn which version of the rule is the real one, and your definition of valid data has one owner. Practical Pydantic covers how to build these reusable types into Python systems, along with the validation patterns around them. 👉 leanpub.com/practical-pydant… #EngineeringLeadership #TechStrategy #SoftwareArchitecture #ProductEngineering #Python
26
You ship on a Friday afternoon. Staging was green, and the checkout dashboard looks fine for an hour. Then the incidents channel lights up: a slice of customers can’t check out, error rates are ugly in one region, and the person who knows the rollback path is already on a train. By Sunday you’ve reverted, filled in the postmortem template, and promised yourself this won’t happen again. At Monday standup, someone says it out loud: “We’ll be more careful next time”. That sounds responsible. It still isn’t a strategy. Care doesn’t scale, and review culture, useful as it is, doesn’t change the shape of the next bad deploy: one binary, all of production, and a hope that you’ll notice fast enough. Canary is one way to shrink that blast radius. For a mid-size product team, the job after the burned weekend is deciding whether it’s your next move (yes, no, or not yet). If you’ve already Googled “canary vs blue green,” you’re in the right place. developer-service.blog/canar…
27
One of the quieter costs of shipping an AI feature fast is that the vendor relationships get decided by default. The team picked a model provider, an embedding service, and a vector store because they were the quickest to get working, and nobody has revisited whether those choices still make sense now that real usage, real costs, and real reliability data exist. The decision was never wrong, exactly. It just was never made on purpose. That matters because switching costs grow quietly. Prompts get tuned to one models quirks, data formats get shaped around one providers API, and an outage or price change that would have been a minor inconvenience at launch becomes a serious conversation six months later. Teams that look at this early still have options. Teams that look late mostly have constraints. If you havent reviewed how tightly your AI feature is tied to a single provider, its worth knowing before the providers next incident or price change forces the question => developer-service.blog/work-… #AI #CTO #TechLeadership #SoftwareEngineering #Python
2
45
Theres a difference between a team that has a process and a team that has a habit. A process is written down, scheduled, and usually followed until a deadline gets tight. A habit survives the deadline. The teams I'd trust with a production AI system are rarely the ones with the most elaborate review checklists. Theyre the ones where someone asks did we check what happens if this fails? Without being prompted, every time, because it has become how they think. That kind of habit can't be installed with a policy. It comes from people who have been burned once and remember it, and from leaders who make asking the awkward question feel normal instead of like an obstacle to shipping. When that culture exists, the technical practices tend to follow: logging, validation, fallbacks, review. When it doesnt, the practices get adopted enthusiastically after an incident and quietly dropped six months later. If you want to know which kind of team you have: developer-service.blog/work-… #SoftwareEngineering #AI #TechLeadership #CTO #Python
17
A good runbook entry for an AI agent is surprisingly short. It names the symptom, the dashboard to check first, the most likely cause, and the one command that rolls it back. Thats it. No theory, no it depends. Heres what that looks like in practice. Symptom: the triage agent starts labeling most issues as high priority. First check: the output distribution chart, compared against last weeks baseline. Likely cause: a model or prompt change that shifted the distribution without breaking the schema. Fix: roll back the canary and rerun the eval suite against the previous version. Writing that entry is easy only if the system was built to support it. You need typed outputs to compare, evals to rerun, and a rollout setup that makes rollback a single step. Thats the idea behind Production AI Agents with PydanticAI: you build a GitHub issue triage assistant chapter by chapter, and each piece of engineering exists so that 3am has a short, boring answer. Free sample chapters: leanpub.com/production-ai-ag… #PydanticAI #LLMOps #AIEngineering #PythonEngineering
1
1
60
AI can write your Python now. It can't understand your scope bugs for you. Copilot, Cursor, ChatGPT: they all generate code that looks right. And most of the time, it is. But when a generated function quietly shadows a variable, or a closure captures the wrong loop value, the code still runs. Tests pass. Review looks clean. The bug ships. The more code we generate, the more it matters that we can read it the way Python does. Not skim it. Reason about it. Spot the line where a name resolves somewhere it shouldn't. That's a fundamentals skill, and AI tools make it more valuable, not less. I put together a free Python Scope Guide covering the LEGB rule, closures, late binding, and global vs nonlocal: so you can review AI-written (and human-written) code with confidence. 🎁 Free: python-variable-scope.develo… #Python #AI #SoftwareEngineering #CodeReview
1
107
Prompt injection is usually discussed as if it were an exotic attack, but in most Python integrations it starts with something mundane: user-supplied or third-party text getting concatenated straight into the prompt with the same authority as your own instructions. A support ticket, a scraped web page, a PDF that went through OCR. Once that text is in the prompt, the model has no reliable way to tell your instructions from instructions someone else embedded in the data. You cant fully solve this at the prompt level, so the defence has to live in your code. Keep untrusted content clearly delimited and labelled as data, give the model the least access it needs to do the job, and never let its output trigger a sensitive action, like sending an email, running a query, or calling an internal API, without checks you control. If a manipulated model response can reach something that matters, the design is the problem, not the prompt. A useful exercise is to list every place untrusted text enters your LLM calls and every action the output can cause, then ask what the worst case is if the model follows the wrong instructions. Most teams that do this find at least one path they didnt know existed. Let me help you validate your design => developer-service.blog/work-… #Python #AI #Security #LLM #SoftwareEngineering
1
3
83