TT Lab
Get started
Learn Learning paths Courses

Debugging in Practice

Fixing an API You Inherited

Continue in TT Lab

Goal

You will be able to find and fix four defects, judging only by the symptoms, in an API handed over to you in a state where neither the tests nor the documentation can be trusted.

Why it matters

The defects in this lab are all of the kind that return 200 while giving a wrong value. Monitoring catches bugs that throw exceptions, but bugs that return plausible-looking numbers are discovered months later when the accounting does not add up. That is why you need the procedure of first turning the spec into an input/output table and trying boundary values.

The status code distinction in the last step is not decoration either. 400 means "you sent it wrong," and 500 means "I broke while handling it," and most client libraries treat only 5xx as retryable. If you return 500 for a wrong parameter, a request that will fail forever is retried endlessly and only raises the server load. A status code is a promise read by machines, not by people.

The original spec is written in the docstring of /opt/app/server.py. The places where the code disagrees with that spec are the bug list.

Steps

  1. Copy /opt/app/server.py to /root/app/server.py. From then on, edit only this copy.
  2. Run the copy so that 127.0.0.1:8000/health responds.
  3. Fix it so that /sum?a=2&b=3 returns 5.
  4. Check that /sum?a=10&b=32 returns 42 and /sum?a=-4&b=9 returns 5.
  5. Fix it so that /avg?nums=2,4,9 returns 5 and /avg?nums=10 returns 10. It is a floor average.
  6. Fix it so that /upper?s=fde returns FDE.
  7. Fix it so that /orders/total returns 130400 with 200. The conditions for a valid row are in the docstring.
  8. Make /sum?a=2 and /sum?a=x&b=1 each return 400. The original code already returns the 404 for /nope, so you only need to check it — the only thing to fix is the argument handling of /sum. The knack of this step is to first measure what is already working.

Notes

Make a working copy

Copy /opt/app/server.py to /root/app/server.py. From then on, edit only this copy.

Not editing the original directly is the basic rule in the field. Copy /opt/app/server.py under /root/app/.

Start the server

Run the copy so that 127.0.0.1:8000/health responds.

If you run it with python3, it waits on 127.0.0.1:8000. You must restart it each time you fix something for the change to take effect.

Fix the addition result

Fix it so that /sum?a=2&b=3 returns 5.

Looking at the value that comes out for 2 and 3 immediately shows which operator was used. Find that one character in the file.

Check negative inputs too

Check that /sum?a=10&b=32 returns 42 and /sum?a=-4&b=9 returns 5.

This is the boundary value check step. If it is right even when the signs are mixed, you know it is truly addition.

Fix the average calculation

Fix it so that /avg?nums=2,4,9 returns 5 and /avg?nums=10 returns 10. It is a floor average.

Try it with three elements and with one element. It shows how far the divisor is off from the number of elements.

Fix the case conversion

Fix it so that /upper?s=fde returns FDE.

Check whether the endpoint name matches the string method that is actually called.

Keep the order total from crashing

Fix it so that /orders/total returns 130400 with 200. The conditions for a valid row are in the docstring.

The docstring lists three conditions for a valid row, but the code has no such branch. Just filter as the document says.

Return the right code for bad requests

Make /sum?a=2 and /sum?a=x&b=1 each return 400. The original code already returns the 404 for /nope, so you only need to check it — the only thing to fix is the argument handling of /sum. The knack of this step is to first measure what is already working.

When the parameter is missing or not a number, the server currently dies. A client's fault is a 4xx.