AI

How I work now with Claude Code Max in August 2025

A real PR walkthrough of my Claude Code setup, plan first and then implement with TDD

Aug 15, 2025 · 6 min read

Photo from reddit

Introduction

A couple of months back, I wrote about my first AI coding setup with Cursor and Claude Code Max. Since then, the way I actually work has shifted quite a bit. Cursor moved to the background, Claude Code moved to the front, and I added claude-context for semantic search over the whole codebase plus a couple of MCP servers on top.

In this post, I want to share a concrete example instead of another list of tools: a real pull request where I added a custom Route53 domain pointing to CloudFront in my cdk-vscode-server construct. Real code I maintain, not a toy. I'll show you the exact prompts, where Claude did great on its own, and the spots where I had to grab the wheel.

What has changed

The setup from the last post got me a working Golang template in under two hours, but I still saw flaws in it. What pushed me further was a mix of things: a workshop in Munich, a few conversations afterward, and, honestly, a lot of reading on Reddit, where people share how they run Claude Code day-to-day.

I was meeting Max Ritter and other enthusiasts for an evening workshop. See his post for more details

Two ideas stuck with me. First, let Claude plan before it writes a single line. Second, make it work test-first. That second one clicked after this thread on TDD with Claude Code, and it matches how I already like to work anyway: write the failing test, then make it green.

New setup

So the driver changed. Claude Code Max now does the heavy lifting, and Cursor is only there for autocompletion. On top, I run a small set of MCP servers:

  • context7 to keep the model current on library APIs
  • the AWS MCP server for CDK and AWS specifics
  • claude-context for semantic code search, so Claude finds the relevant construct without reading half the repo first

Personally, I don't want a huge toolbox. These three cover the gaps I actually hit: fresh docs, AWS knowledge, and finding my way around a bigger codebase.

Walkthrough

Two commands do most of the steering. /mcp shows me which MCP servers are connected, so I can confirm context7, the AWS server, and claude-context are all live before I start.

For the model and the mode, I use /model to pick between Opus and Sonnet, and SHIFT+TAB to cycle the modes until I land on plan mode. Plan mode is the important one here: Claude reasons through the change and writes a plan without touching a single file yet.

Auto mode

My flow is plan first, then let it run. I start in plan mode on Opus and ask the open question:

How would you add custom Route53 domain pointing to cloudfront

I read the plan, and only when I'm happy with it do I hand it back for implementation, this time asking for test-driven development:

Use this plan to implement it with TDD (unit tests)

What came back was a clean red-green plan, tests first, implementation after:

  1. Write failing unit tests for custom domain properties (TDD Red phase)
  2. Write failing tests for Route53 record creation
  3. Write failing tests for CloudFront custom domain configuration
  4. Write failing tests for ACM certificate handling (existing + auto-create)
  5. Write failing tests for hosted zone lookup and validation
  6. Write failing tests for custom domain output and backward compatibility
  7. Extend VSCodeServerProps interface with new domain properties
  8. Add required CDK imports for Route53, ACM, and related services
  9. Implement domain validation and configuration logic
  10. Implement ACM certificate handling (existing vs auto-create)
  11. Implement Route53 hosted zone lookup and A record creation
  12. Update CloudFront distribution with custom domain configuration
  13. Update domain name output to use custom domain when provided
  14. Add CDK-nag suppressions for new resources
  15. Run tests to ensure all pass and no regressions
  16. Create example implementation with custom domain

The first six steps are all failing tests. That is exactly the order I would have written it in myself.

And then it worked through the list until the tests went green:

Manual intervention

This is the part people leave out of the "look how fast AI is" posts. Left completely alone, this change would have broken later.

The first one was the nginx config. The installer sets an nginx server_name for the CloudFront domain, and for a custom domain, which would have failed during the integration tests. So I pointed Claude straight at it:

In installer.ts, there is a config for nginx with a servername for CloudFront. Fix it so it also works with the provided domain.

Then the integration tests themselves. These are expensive: they create real resources and take time, so I add them at the very end, and I don't let them run automatically:

Add an integ test for the custom domain (do not run it). Assume that the testdomain is 'vscode-server-test.mavogel.xyz' with the hostedZoneId 'Z123456789EXAMPLE

From my rootmail project I already knew that lookup functions don't work in integ-tests, so I kept an eye on that one.

At this point, the whole custom-domain feature was done in 26 minutes.

Now the integ-tests. Running the dry-run surfaced an error:

Fix the error when running npm run integ-test -- --dry-run

And here is where I had to be firm. Claude wanted to simply remove the NodeTagger to make the error go away. That is the classic move: delete the inconvenient thing. I didn't want that. The tagging is there on purpose:

Keep the NodeTagger and fix the issue with the priority

The final version keeps the aspect and is careful about which constructs it tags, which avoids the infinite loop that caused the error in the first place:

/**
 * Tags all the resources in the construct
 */
class NodeTagger implements IAspect {
  private readonly tags: { [key: string]: string };
 
  constructor(tags: { [key: string]: string }) {
    this.tags = tags;
  }
 
  visit(node: IConstruct) {
    // Only tag L1 constructs (CfnResource) and L2 constructs that represent AWS resources
    // This prevents infinite loops by avoiding tagging of intermediate constructs
    const nodeType = node.constructor.name;
 
    // Check if this is a CDK L1 construct (starts with 'Cfn') or known taggable L2 constructs
    const isTaggableConstruct =
      nodeType.startsWith('Cfn') ||
      nodeType.includes('Instance') ||
      nodeType.includes('Vpc') ||
      nodeType.includes('Subnet') ||
      nodeType.includes('SecurityGroup') ||
      nodeType.includes('Volume') ||
      nodeType.includes('Distribution') ||
      nodeType.includes('LoadBalancer') ||
      nodeType.includes('TargetGroup');
 
    // Skip constructs that are known to cause issues
    const isProblematicConstruct =
      nodeType.includes('Certificate') ||
      nodeType.includes('HostedZone') ||
      nodeType.includes('CustomResource') ||
      nodeType.includes('Provider') ||
      nodeType.includes('Function') ||
      nodeType.includes('Role') ||
      nodeType.includes('Policy');
 
    if (isTaggableConstruct && !isProblematicConstruct) {
      Object.entries(this.tags).forEach(([key, value]) => {
        try {
          Tags.of(node).add(key, value);
        } catch (error) {
          // Silently ignore tagging errors
        }
      });
    }
  }
}

If there is one thing that still annoys me in all of this, it is CDK integration tests. They are slow, and they cost real money, and they are exactly the part where the AI still needs the most supervision.

Conclusion

Personally, I'm convinced the skills that matter now are prompt and context engineering, not typing every line yourself. Give the model a plan you actually reviewed, the right context via MCP servers, and a guardrail like TDD, then check every step, as you would review a junior's pull request. The 26 minutes only happened because I knew what I wanted and stepped in the second it drifted.

You can see the full result in the summary PR: MV-Consulting/cdk-vscode-server#63.

I hope this gives you a realistic picture of a plan-first, test-driven session, the good parts and the moments where you still have to take over yourself. Happy prompting!

More from the blog

Keep reading - related notes from recent engagements.

DevOps

Advanced GitLab CI features in v19

A tour from job grouping to dynamic child pipelines, and a look at the experimental functions feature, every example run on a real GitLab 19 instance.

Ready to ship faster on AWS?

Tell us what you are building. We will map the fastest safe path to production and the platform to keep it there.

Notes from production

Occasional, no-fluff writing on AWS, DevOps, and running platforms that stay up. No spam, unsubscribe anytime.

Practical, not promotional
Real lessons from real engagements - architecture, automation, and incident post-mortems.
No spam
A few emails a year at most. Your address is never shared or sold.