Senior Backend Engineer and Senior AI Engineer at Indonesia’s legal information platform, working on a serverless MVP, retrieval over case law, critical fixes, and observability. [Golang, Python, AWS].
Hukumonline is Indonesia’s legal information platform, publishing regulations, court decisions, and legal analysis, alongside services for legal professionals and businesses.
The work splits across both roles: a greenfield service on a serverless stack, retrieval over a corpus of court decisions, two critical bugs found early on, and the observability groundwork that makes all of it debuggable.
An adapter translates the API Gateway event into a standard http.Request and hands it to the router, which means the handlers are written exactly as they would be for any HTTP service. The same binary runs locally as a plain server, so the serverless deployment target never leaked into the application code — which is what let the MVP ship on a short timeline.
Fun fact: cost was part of the brief — the MVP was meant to run at as close to zero infrastructure cost as possible, and on this stack that is more achievable than it sounds. Lambda’s free allowance is 1 million requests and 400,000 GB-seconds of compute per month, and DynamoDB’s is 25 GB of storage with 25 read and 25 write capacity units in provisioned mode — roughly 200 million requests a month, depending on item size. Neither allowance expires: these are the always free tiers, not the 12-month trial. The front door is the part that does run out, since API Gateway’s free tier lasts only 12 months — which is what makes a Lambda Function URL the genuinely free entry point.
The diagram above is a deliberate simplification — it shows the spine of the pipeline, not the system. The production version carries considerably more than a single retrieve-then-answer pass.
Grounding matters more than usual in this domain: a legal answer that cannot be traced back to a specific decision is not useful, so the retrieved passages are the answer’s evidence, not just its prompt.
The keys are an asymmetric pair, and the public half changes rarely — so paying a per-request API call to re-read something that had not changed made the bill scale with traffic rather than with need. The fix was less about caching than about moving the read to the place it belonged: process startup.
This is the same groundwork I introduced at eFishery — it has turned out to be the highest-leverage thing to bring into a team that does not have it yet, because it changes debugging from reading separate logs and guessing into reading one trace.
The transaction handling follows the approach I wrote up earlier: Database Transaction Implementation Techniques in Logic Layer for Golang Backend — keeping transaction control in the service layer so the business logic stays independent of the database driver.