Technology choices for AI development

Our technology stack is chosen to make AI-assisted development more reliable. These are not arbitrary preferences; they are decisions that make LLM tooling easier to use well.

Why Python for AI-assisted development

Python is not merely popular. It is structurally better suited for LLM-assisted development than alternatives such as C# or Ruby. Here is why:

Close to natural language

Python's syntax closely resembles natural language with keywords like in, is, not, and, or.

When an LLM sees if x in collection: versus C#'s if (collection.Contains(x)), the Python version maps more directly to how the problem would be described in English.

Less syntactic noise

Python's lack of mandatory type declarations, braces, and semicolons means less syntactic noise for an LLM to generate correctly.

C# requires precise placement of curly braces, semicolons, access modifiers and type annotations. That means more chances for small errors that break compilation.

Smaller context-window footprint

Python code is typically 30-50% more concise than equivalent C# code.

That means an LLM can fit more of the codebase into its context window, which matters when it must understand relationships between files or make cross-cutting changes.

Training-data depth

LLMs have seen orders of magnitude more Python code across more domains than any other language.

This statistical advantage means the LLM has seen similar patterns before: web development, data science, automation, AI/ML and scripting.

One obvious way

Python's philosophy of "there should be one—and preferably only one—obvious way to do it" means LLMs encounter more consistent patterns across codebases. This predictability reduces the search space for correct solutions.

Python vs C# vs Ruby for LLM Work

A fair comparison of the three languages most commonly considered for enterprise web development:

FactorPythonRubyC#
LLM Training Data VolumeExcellentGoodModerate
Syntax SimplicityExcellentGoodVerbose
Pattern ConsistencyHighVariableVariable
Context Window EfficiencyExcellentGoodPoor
Type Safety (Error Prevention)OptionalNoneStrong

Python: best overall

The combination of training-data volume, syntactic simplicity and domain breadth is hard to beat. LLMs working with Python benefit from exposure across web, data science, automation and AI/ML.

Ruby: close second

Ruby's expressiveness and "everything is an object" consistency create fewer edge cases. Convention-over-configuration, especially in Rails, helps LLMs reuse learned patterns.

C#: distant third

While C#'s type system provides guardrails, the cognitive overhead is significant. Mixing OOP patterns, LINQ, async/await, and DI frameworks requires maintaining multiple philosophies simultaneously.

The honest caveat

For a large existing enterprise codebase with complex business logic, C#'s type system may flip this ranking. Compile-time checks catch LLM errors that would become runtime failures in Python. But for new development where AI assistance matters most, Python's advantages dominate.

Django: batteries included, decisions made

We favour "batteries included" frameworks where sensible defaults reduce decision fatigue. Django is a good example and it compounds the benefits of AI-assisted development.

Convention over configuration

  • ORM with migrations built-in
  • Authentication system ready to use
  • Admin interface auto-generated
  • Form handling and validation included
  • Security middleware by default
  • Templating engine built-in

Why this helps LLMs

  • Consistent project structure across codebases
  • Standard patterns LLMs have seen thousands of times
  • Less custom code = fewer surprises
  • Documentation and examples abundant
  • Fewer integration decisions to get wrong
  • Mature ecosystem with proven solutions

The pragmatic choice

Every decision you do not have to make is a decision you cannot get wrong. Django makes hundreds of small decisions for you: database abstraction, session handling, CSRF protection and password hashing.

When an LLM generates Django code, it follows patterns established over 18 years and tested by millions of applications. Boring, in this case, is a virtue.

The pragmatic monolith

Microservices have their place. For most applications, that place is "later, if ever." We build monoliths first not because we do not understand distributed systems, but because we do.

Why monoliths suit AI-assisted development

Single codebase, full context

An LLM can understand your entire application. With microservices, context is fragmented across repositories, and the AI can't see the relationships between services.

Simpler debugging

When something breaks, there's one place to look. No distributed tracing, no "which service owns this?" conversations, no network partitions to diagnose.

Faster iteration

Change your data model and update every affected view in one commit. No API versioning, no backwards compatibility gymnastics, no coordinated deployments.

Lower operational burden

One deployment pipeline. One monitoring dashboard. One database to backup. One thing to scale. The operational simplicity compounds over time.

Team efficiency

Small teams thrive with monoliths. You don't need a platform team, an infrastructure team, and service owners. You need developers who ship features.

Refactoring freedom

Want to restructure your domain? Do it. Move code between modules without worrying about API contracts, service boundaries, or breaking other teams.

The monolith scales

Shopify runs on a monolith. GitHub ran on a monolith for years. Basecamp still does. These are not small applications.

A well-structured monolith can handle enormous scale. Premature decomposition into microservices usually creates problems, not solves them.

Modular monolith, future options

Build with clear module boundaries inside your monolith. If you genuinely need to extract a service later, you can.

But you probably won't need to. And if you do, you'll know exactly where to cut because you understand your domain—not because you guessed upfront.

When microservices make sense

We're not dogmatic. Microservices are appropriate when:

  • • You have genuinely independent scaling requirements (not "might need to scale differently someday")
  • • You have multiple large teams that need to deploy independently
  • • You have regulatory requirements for isolation (payment processing, healthcare data)
  • • You're integrating systems written in different languages that can't share a runtime

If none of these apply, start with a monolith. You can always evolve later with better information.

Infrastructure: Terraform on Google Cloud

Our default infrastructure stack is Terraform on Google Cloud Platform. It is not the only option, but it is a good default with few downsides.

Why Terraform

  • Infrastructure as Code: Version controlled, reviewable, repeatable
  • LLM-Friendly: Declarative HCL syntax that AI understands well
  • Cloud Agnostic: Same patterns work across GCP, AWS, Azure
  • State Management: Track what exists, plan changes safely
  • Mature Ecosystem: Modules for common patterns readily available

Why Google Cloud

  • Cloud Run: Container deployment without Kubernetes complexity
  • Cloud SQL: Managed PostgreSQL with automatic backups
  • Simpler IAM: More intuitive than AWS's permission model
  • Competitive Pricing: Often cheaper than AWS for similar workloads
  • Excellent AI/ML: Vertex AI, BigQuery ML for future expansion

The complete stack

Language

Python 3.11+

Framework

Django / FastAPI

Infrastructure

Terraform + GCP

Database

PostgreSQL

Boring technology, chosen deliberately. Every component is well-understood, well-documented and well-supported by LLMs.

Ready to build with a pragmatic stack?

We will help you move faster with technology choices that make AI-assisted development safer and more useful.

Get in touch