Removing an empty dictionary can look like a harmless refactor. In a DynamoDB application, it can break the next write.
That was one of two bugs explored during our October 28th Quito Lambda session, LocalStack: A safe environment for experimenting with AI agents and AWS. Gandhy García, a software developer at Stack Builders, used a small game API to show an AI coding agent reproducing failures, changing code, and running integration tests against local AWS service emulation.
The interesting part was the feedback available to the agent. It could exercise the application's database operations without needing credentials for a live AWS environment. A test could fail because the application misunderstood DynamoDB behavior, rather than pass because a mock accepted whatever the code sent.
The session offers a practical approach to AI-assisted debugging: give the agent a repeatable test environment that exposes meaningful failures, and control where its commands can operate.
When a mock misses the database behavior
The demo application implemented Chameleon, a game in which one player lacks the secret word known to the others. Built with Python and FastAPI, it supported creating games, joining them, starting play, casting votes, and retrieving results.
Each game occupied one DynamoDB item containing players, the host, the secret word, game status, and votes. This was a deliberately simple demonstration model, rather than a proposed design for every production game.
Its voting operation crossed an important boundary. An endpoint called a repository function, cast_vote, which used DynamoDB's update_item operation and conditional expressions to change stored game data.
Mocking that repository call can help test the endpoint's handling of a response. It does not establish that DynamoDB will accept the actual update expression. If the mock always returns success, a database-specific failure can remain invisible.
LocalStack emulates AWS services locally. In this session, it supplied DynamoDB behavior through an endpoint the existing Python SDK could call. The integration tests exercised the application's repository code against that emulated service, giving the debugging process more evidence than a stubbed response alone.
LocalStack DynamoDB testing with the existing SDK
The setup used Docker Compose to run a version-pinned LocalStack image with DynamoDB enabled. The application continued to use boto3, the AWS SDK for Python. It did not need a separate LocalStack SDK.
The important change was configuration: a local endpoint on port 4566, a selected region, and dummy AWS access credentials. A separate .env.test file kept the test settings apart from the application's ordinary environment file.
In conftest.py, the pytest configuration loaded those test settings using Pydantic. That made local service selection part of the test harness, rather than a decision the agent had to remember on each run.
Beyond the session, LocalStack's boto3 documentation confirms that the standard client can target a local endpoint. Dummy credentials alone do not select that endpoint: routing and credentials must both be configured correctly.
The setup also distinguished the LocalStack authentication token used to run the emulator from the dummy AWS credentials used by the application. Those serve different purposes. Local AWS emulation does not make every configuration value disposable or safe to expose.
A missing map breaks the first fix
The first debugging exercise began from a prepared bad refactor. It removed the empty votes dictionary created with each game.
That dictionary mattered because the repository later wrote entries beneath it. With the parent map absent, the nested update no longer worked. Four existing tests were failing before the fix.
The agent ran the suite, identified the refactor as the cause, and restored the initialization. During the walkthrough, it reported that all 30 tests passed after the change, alongside a formatting check.
This is a specific database rule, rather than a general requirement to initialize every possible field. As supporting context beyond the demo, AWS documents that a nested map attribute cannot be updated when its parent map does not exist.
For this implementation, the empty map was a precondition for a later operation. The integration test made that relationship observable. Removing it changed behavior even though the surrounding application logic could still look reasonable.
A self-vote reveals a missing rule
The second exercise addressed a different failure: players could vote for themselves.
The application already rejected a player who tried to vote a second time. That existing condition worked, but it did not cover self-voting. In the demo, Bob voted for Bob and received HTTP 204, indicating the request had succeeded.
Gandhy asked the agent to reproduce the bug with a test and fix it. The walkthrough showed new tests and a repository condition comparing the voter with the target player. The agent ran tests against LocalStack as part of that work.
The fix was then checked through the API. Carol's attempt to vote for Carol returned an error. Carol could still vote for Alice, which returned 204.
Together, these checks established a useful distinction. The first bug violated a database precondition; the second violated a game rule that had never been enforced. Service emulation can expose storage behavior, but engineers still need to identify the behavior the product requires and review whether the tests represent it.
Keep test data separate from debugging data
The test harness created a temporary table for each pytest session, with a random identifier in its name. It deleted that table after the suite finished.
The manually seeded game table remained available in the same LocalStack instance. Gandhy could inspect games and try API requests while the test suite created and removed its own resources.
This separation made the agent's test runs easier to repeat without disturbing the demonstration data. It also made resource activity visible: table creation, test writes, and table deletion appeared in the local logs.
For AWS local testing, the general lesson is to make the setup and cleanup explicit. Temporary resources should belong to the test run that created them, with enough separation that a cleanup step cannot casually remove unrelated development data.
A local service is one layer of the boundary
The session repeatedly qualified its safety claims. Running LocalStack does not, by itself, isolate the coding agent from the rest of the machine or prevent it from reaching AWS through another command.
The demonstrated approach combined agent instructions, command permissions, separate test configuration, and local services. Repository guidance told the agent to run tests and avoid real credentials. Agent settings supplied another layer of command restrictions.
These layers serve different purposes. Instructions guide behavior; permissions restrict available actions; the test configuration selects the destination. A local emulator cannot protect credentials that remain available elsewhere to an unrestricted agent.
As an engineering recommendation beyond the demo, keep live cloud credentials out of the agent's test environment and make unexpected remote endpoints cause test setup to fail. Review both the code change and any altered tests. Passing tests are weaker evidence if the agent has simply removed the assertion that exposed the bug.
Check coverage before relying on the local result
An audience question raised workflows that combine emulated services with cloud dependencies. That introduces a separate boundary: some calls may be local while others still reach AWS.
The Q&A highlighted that LocalStack can run alongside real AWS services. An application could, for example, use a local DynamoDB table while calling Amazon Bedrock in AWS. Available LocalStack services vary by plan and tier, so configuration needs to make clear which requests use local endpoints and which reach AWS. This mixed setup allows teams to test parts of a workflow locally while retaining access to real services where needed.
Local tests also leave cloud deployment behavior to verify. They do not establish production performance or replace validation in the target AWS environment.
Licensing matters too. LocalStack's current pricing guidance restricts the free Hobby plan to non-commercial use. Avoiding AWS resource charges does not necessarily mean a commercial development workflow has no tooling cost. For a DynamoDB-only requirement, DynamoDB Local is another option to evaluate, as an additional reference beyond the demonstrated setup.
Start with one failure that depends on actual service behavior. Reproduce it locally, let the agent propose a fix, and verify both the intended result and the surrounding rules. That makes AI agent testing useful because it supplies evidence an engineer can inspect.
Watch the Quito Lambda recording (in Spanish) for both debugging exercises.
Top comments (0)