Back to Blog
Published on

LLMs in Your Stack: When AI Accelerates Development (And When It Doesn't)

AI DevelopmentSoftware EngineeringLLMsTechnical LeadershipDevelopment Workflow
LLMs in Your Stack: When AI Accelerates Development (And When It Doesn't)

Your team just shipped a feature in three days that would have taken two weeks. The code works. Tests pass. But six months later, you're debugging cryptic logic that no one-including the original developer-fully understands.

This is the AI acceleration trap. LLMs like GPT-4 and Claude can 10x your output. But they can also 10x your technical debt if you don't know where to point them.

I've spent the last 18 months integrating LLMs into our development workflow at BlueBerryBytes. Not as a product feature. As a development tool. The results are real: faster sprints, fewer meetings, and-surprisingly-higher code quality. But only because we learned the hard way what LLMs are genuinely good at versus where they'll burn you.

Here's the playbook.

The Mental Model: LLMs as Junior Developers with Perfect Memory

Stop thinking of LLMs as magic. Think of them as exceptionally competent junior developers with two superpowers:

  1. Perfect recall of every API, framework, and pattern they've seen.
  2. Zero ego about doing repetitive work.

And two critical weaknesses:

  1. No architectural judgment. They'll write whatever you ask, even if it's structurally wrong.
  2. No concept of "good enough". They optimize for completeness, not maintainability.

This mental model changes everything. You wouldn't let a junior architect your system. But you'd absolutely let them scaffold boilerplate, write tests, or refactor repetitive code-under supervision.

Where LLMs Accelerate: The 70% Rule

LLMs excel at the 70% of development work that's mechanically correct but intellectually boring. Here's where we've seen measurable gains:

1. Boilerplate Generation

Writing CRUD endpoints, form validation, or database migrations is soul-crushing. LLMs handle this in seconds.

Example: We needed to add a new entity to an existing Node.js/Prisma API. Instead of spending 30 minutes writing the schema, migration, service layer, and controller, I fed Claude the existing pattern:

// Prompt: "Add a new 'Project' entity with fields: name (string), // description (text), status (enum: draft/active/archived), // createdAt, updatedAt. Follow the User entity pattern." // Output (slightly cleaned): model Project { id String @id @default(cuid()) name String description String @db.Text status Status @default(DRAFT) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } enum Status { DRAFT ACTIVE ARCHIVED }

The service layer, routes, and validation followed the same pattern. Total time: 5 minutes. Key point: I still reviewed every line. The LLM saved time, not judgment.

2. Test Coverage

Writing unit tests is where most teams fall behind. LLMs can generate comprehensive test suites based on existing code.

// I gave GPT-4 this function: function calculateDiscount( price: number, userTier: "basic" | "premium" | "enterprise", ): number { if (price <= 0) throw new Error("Invalid price"); const multipliers = { basic: 0.05, premium: 0.1, enterprise: 0.15 }; return price * multipliers[userTier]; } // It generated 12 test cases including edge cases I hadn't considered: describe("calculateDiscount", () => { it("should throw error for negative prices", () => { expect(() => calculateDiscount(-10, "basic")).toThrow("Invalid price"); }); it("should throw error for zero price", () => { expect(() => calculateDiscount(0, "premium")).toThrow("Invalid price"); }); it("should handle decimal prices correctly", () => { expect(calculateDiscount(99.99, "premium")).toBeCloseTo(9.999, 2); }); // ... 9 more cases });

We went from ~40% test coverage to 85% in two weeks by systematically feeding untested functions to Claude. The catch? You still need to verify the tests are meaningful, not just comprehensive.

3. Documentation and Comments

LLMs are exceptional at writing clear, context-aware documentation.

# Before (written by human): def process_batch(items, callback): # Process items results = [] for item in items: results.append(callback(item)) return results # After (Claude-generated docstring): def process_batch(items: List[Dict], callback: Callable) -> List[Any]: """ Processes a batch of items using a callback function. Args: items: List of dictionaries representing data items to process callback: Function that takes a single item dict and returns processed result Returns: List of processed results in the same order as input items Raises: TypeError: If items is not iterable or callback is not callable Example: >>> items = [{'id': 1, 'value': 10}, {'id': 2, 'value': 20}] >>> results = process_batch(items, lambda x: x['value'] * 2) >>> print(results) # [20, 40] """ results = [] for item in items: results.append(callback(item)) return results

We run a weekly script that feeds poorly-documented functions to an LLM and generates docstrings. Developers review and commit. Documentation debt: solved.

4. Legacy Code Translation

Migrating from one framework to another is a nightmare. LLMs can handle the mechanical translation while you focus on architectural decisions.

We recently migrated a client's Django REST API to FastAPI. The LLM translated 80% of the routes in an afternoon:

# Django (before): from rest_framework.decorators import api_view from rest_framework.response import Response @api_view(['POST']) def create_order(request): serializer = OrderSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=201) return Response(serializer.errors, status=400) # FastAPI (after, LLM-generated): from fastapi import APIRouter, HTTPException from pydantic import BaseModel router = APIRouter() class OrderCreate(BaseModel): # LLM inferred fields from Django serializer customer_id: int total: float items: List[str] @router.post("/orders", status_code=201) async def create_order(order: OrderCreate): try: # LLM correctly identified the save logic saved_order = await save_order_to_db(order) return saved_order except ValidationError as e: raise HTTPException(status_code=400, detail=str(e))

The translation was 90% correct. We spent our time on the 10%: fixing async patterns, optimizing queries, and restructuring the auth layer.

Where LLMs Fail: The 30% That Matters

LLMs break down when tasks require architectural judgment, business context, or system-wide thinking. Here's where they'll waste your time:

1. System Design

Never ask an LLM to design your architecture. They'll give you a textbook answer that ignores your constraints.

Bad prompt: "Design a microservices architecture for an e-commerce platform."

Result: You'll get a 15-service nightmare with message queues, event sourcing, and CQRS. Beautiful on paper. Unmaintainable in reality for a 5-person team.

Better approach: Design the architecture yourself. Use the LLM to validate trade-offs or fill gaps.

Good prompt: "I'm building an order processing system. Should I use a queue or direct database writes? We have 10K orders/day, 2 backend engineers, and need to ship in 6 weeks."

The LLM will give pros/cons. You make the call.

2. Performance Optimization

LLMs write code that works, not code that scales. They'll generate an O(n²) algorithm when O(n log n) is needed because they don't profile your workload.

We asked GPT-4 to optimize a slow search function. It suggested adding indices and caching-generic advice. The real bottleneck? A hidden N+1 query in the ORM. We found it by profiling, not prompting.

3. Debugging Production Issues

LLMs can't access your logs, metrics, or system state. They're useless for debugging unless you feed them every relevant piece of context-which takes longer than debugging yourself.

Exception: They're excellent at explaining cryptic error messages or suggesting diagnostic commands.

# Error: "SSL: CERTIFICATE_VERIFY_FAILED" # Prompt: "Explain this SSL error in a Node.js app on AWS Lambda." # Output: Clear explanation + 3 troubleshooting steps.

4. Business Logic

LLMs don't understand your domain. If you prompt "Calculate shipping cost," they'll invent rules. You'll catch obvious mistakes, but subtle bugs will slip through.

Rule: Never trust LLM-generated business logic without verification from a domain expert.

The Workflow: How We Actually Use LLMs

Here's our day-to-day process:

1. Prototyping (High Trust)

In the prototype phase, speed matters more than perfection. LLMs write entire features. We review structure, not syntax.

Example: Building an MVP dashboard. Claude generated 80% of the React components. We focused on UX decisions and API contracts.

2. Implementation (Medium Trust)

During active development, LLMs handle boilerplate, tests, and docs. Engineers write core logic.

Checklist:

  • ✅ CRUD endpoints → LLM
  • ✅ Validation schemas → LLM
  • ✅ Test suites → LLM (reviewed)
  • ❌ Auth logic → Human
  • ❌ Payment flows → Human
  • ❌ Data migrations → Human (LLM drafts, human reviews)

3. Refactoring (Low Trust)

When refactoring critical paths, LLMs assist but don't lead. We use them to generate alternative implementations, then we choose.

Prompt: "Refactor this 200-line function into smaller, testable units."

Output: LLM splits the function into 5 smaller ones. We validate the logic hasn't changed by running the full test suite.

4. Code Review (Augmentation)

LLMs are decent at catching obvious issues: unused variables, missing error handling, inconsistent naming. They're terrible at catching logical errors.

We run Claude as a pre-commit check:

# .git/hooks/pre-commit git diff --cached --name-only | grep ".ts$" | xargs -I {} \ llm "Review this TypeScript file for code quality issues: $(cat {})"

It flags ~30% of issues. Humans catch the rest.

We don't use one LLM. We use the right tool for the job:

  • GPT-4: Best for structured tasks (API design, schema generation, test writing). Expensive but reliable.
  • Claude 3.5: Best for long-context tasks (refactoring large files, explaining legacy code). Handles 100K tokens.
  • Cursor/Copilot: Best for inline suggestions. Speeds up repetitive typing.
  • Aider: CLI tool for pair programming. We feed it diffs, it generates patches.

Cost: ~$200/month for a 5-person team. Easily offset by saved hours.

The Rules: What We Learned the Hard Way

After 18 months, here's what works:

Rule 1: Never Commit LLM Code Unreviewed

Treat LLM output like a junior's PR. It needs review, testing, and validation.

Rule 2: Pair LLMs with Tests

If you can't test it, don't let the LLM write it. Untested LLM code is technical debt.

Rule 3: Optimize for Edit Distance, Not Raw Output

Don't ask LLMs to write 500-line modules from scratch. Ask them to modify existing code. Lower error rate, easier to review.

Rule 4: Use LLMs to Explain, Not Decide

LLMs are Socratic assistants, not architects. They help you think, not replace thinking.

Rule 5: Track Time Savings

Measure before/after. If a task doesn't save 50%+ time, the LLM isn't the right tool.

The Bigger Picture: AI as Infrastructure

Here's the contrarian take: LLMs aren't replacing developers. They're replacing the 70% of development work that shouldn't require a developer.

Think about it. Before AWS, you hired a sysadmin to rack servers. Now you spin up EC2 instances. AWS didn't replace sysadmins-it replaced the undifferentiated heavy lifting. The best sysadmins became cloud architects.

LLMs are doing the same for code. They're handling the boilerplate, the tests, the docs-the work that's necessary but not creative. This frees engineers to focus on what actually matters: architecture, trade-offs, and domain modeling.

If you're still spending 40% of your sprint writing CRUD endpoints, you're stuck in 2020. The teams that win in 2025 are the ones that treat LLMs as tooling infrastructure-like CI/CD or version control. You wouldn't build software without Git. Soon, you won't build it without an LLM in your workflow.

The Real Question: What Are You Doing With the Time You Save?

This is where most teams fail. They use LLMs to ship faster, but they don't change what they ship. They just accumulate more features. More debt. More complexity.

We use the saved time to do what LLMs can't: Deep architectural work. Talking to users. Simplifying systems. Reducing dependencies.

If your LLM-accelerated team is just shipping more Jira tickets, you're missing the point. The goal isn't to do more. It's to do better.

Book a Free Rescue Call