Skip to content
Rohit Behera

Open to backend roles · available now · Chennai, India

The expensive bugsdon’t crash.

I’m Rohit Behera, a backend engineer. I build Java and Spring Boot services where a retry can’t run twice, and I fix the failures that pass silently in the open-source libraries underneath them, including the ones that feed text to LLMs.

trace · onboarding pipeline · bureau returns 503
  1. 00:00.000infoPOST /api/v1/applications Idempotency-Key: k-CC-3MXP
  2. 00:00.002infofilters: correlation id → bearer auth → rate limit
  3. 00:00.004okone transaction: applicant + application + outbox_events(PENDING) + audit
  4. 00:00.005ok202 Accepted CC-3MXP (no vendor called yet)
  5. 00:02.000okpoller → application.created outbox row PUBLISHED
  6. 00:02.004infodelivered to bureau-check-queue
  7. 00:02.007infostatus guard: RECEIVED ✓
  8. 00:02.127okKYC/AML verified before any bureau spend
  9. 00:02.217warnattempt 1/3: 503 Service Unavailable
  10. 00:02.217warnretry in 1s (exponential backoff)
  11. 00:03.307warnattempt 2/3: 503 Service Unavailable
  12. 00:03.307warnretry in 2s (exponential backoff)
  13. 00:05.487okattempt 3/3: bureau score 718
  14. 00:05.489infopublish bureau.completed (after commit)
  15. 00:05.492infoevaluate: score, debt-to-income, policy version
  16. 00:05.498okAPPROVED → decision_records + status in one tx
  17. 00:05.500infopublish decision.approved (after commit)
  18. 00:05.504infoclaim: DECISION_MADE → CORE_BANKING_PENDING (flushed before the call)
  19. 00:05.744okprovision account → ACCOUNT_CREATED
  20. 00:05.748okmirror row written for the legacy platform
  21. 00:05.751okCOMPLETED
simulated · real retry and breaker configBreak it yourself ↓

Selected work

5 case studies

Hard part first, then the evidence.

  1. 01Personal capstone2026

    Credit-card onboarding pipeline

    A duplicate message here is not wasted work. It is a second hard enquiry on a real person's credit file.

    • Java 17
    • Spring Boot 3.5
    • PostgreSQL 15
    • RabbitMQ
    • Resilience4j
    • Flyway
    • 7 ADRs, including the ones that record what is not built yet
    • 138 tests; one runs the whole pipeline against real Postgres and RabbitMQ
    • 8 Prometheus alert rules, 4 Grafana dashboards
  2. 02Own open source · MIT2026

    splitlint

    The broken chunk still joins back into the original string. Nothing throws; retrieval just gets worse for one language.

    • Python
    • Unicode UAX
    • pytest
    • GitHub Actions
    • 766 of 766 official Unicode grapheme conformance cases pass
    • LangChain's RecursiveCharacterTextSplitter fails 10 of its 12 cases
    • Grew out of an upstream fix in langchain4j
  3. 03Open-source contribution2026

    AST-RAG: code retrieval for AI agents

    Text chunks cannot answer "who calls this?". The graph can, but only if every file is parsed correctly and the parser is safe to share between threads.

    • Python
    • Tree-sitter
    • Neo4j
    • Qdrant
    • SQLite
    • Most of the repository's commit history is mine
    • Parse cache: content-addressed, bounded, two backends
    • Fixed a data race that corrupted results instead of crashing
  4. 04Own open source · MIT2026

    Plumb

    Ask an agent whether the feature works and it will say yes, sincerely, having verified nothing. "Done" was never defined, so the only available answer is a feeling.

    • Python 3.10+
    • Claude Code hooks & skills
    • mypy --strict
    • GitHub Actions
    • Zero runtime dependencies, no network, no telemetry
    • Uninstall removes only files whose hash it recorded
    • Never changes the user's permission mode
  5. 05Professional · a global bank2023–2026

    Legacy service modernization

    Behaviour had to be provably identical before and after, including the parts nobody had documented, because the thing on the other end of the endpoint is somebody's account.

    • Java
    • Spring Boot
    • PostgreSQL
    • REST
    • SOAP
    • Elasticsearch
    • 17+ endpoints migrated, ≈35% of the programme
    • Test coverage raised to 80% by writing the test first
    • Legacy maintenance overhead down ≈25%

Failure lab

interactive

Break the pipeline.

The onboarding service, simulated from its real configuration: retry counts, backoff, breaker thresholds, the outbox poll interval. Pick a failure. Every path ends in a named, audited state, except the one marked as a gap, which is written up with its fix.

Failure lab· simulated from the service’s config

Inject a failure

The bureau stops answering while applications keep arriving.

After 5 failed calls the circuit opens and later applications fail fast instead of piling onto a vendor that is already struggling. Each lands in a visible, audited failed state, not limbo.

In request

  1. Client

    mobile app

  2. Intake API

    filters, validation

  3. Postgres

    app + outbox, 1 tx

After 202

  1. Outbox poller

    every 2s

  2. RabbitMQ

    queue per stage

Stages

  1. KYC

    before any spend

  2. Bureau

    retry + breaker

    circuit closed

  3. Decision

    pure evaluator

  4. Core banking

    claim, then call

  5. Legacy mirror

    vendor row

On nack

  1. Dead letters

    audited failure

Status—

    AI engineering

    retrieval, end to end

    The data path into an LLM.

    Most of my AI work is on the unglamorous side of retrieval: getting text in without losing it, cutting it without corrupting it, indexing structure, measuring whether the right passage comes back, and making agents prove their work. Each stage below links the evidence.

    1. 01Extract

      Turn HTML, PDFs and office files into text.

      Breaks:Content is dropped or garbled, and the document still parses.

      • trafilatura #940mergedText directly after a list, table, quote or code block was dropped from every output format.
      • unstructured #4502mergedDefinition lists were discarded from HTML, so a Sphinx API reference page partitioned to its headings and nothing else.
      • unstructured #4449mergedspaCy ran three times on the same text per element: 88% of partition_html() time in the profile. Now once.
      • docling #3985mergedCSV conversion failed when the first quoted field spanned a newline: the dialect was sniffed from a truncated line.
      • docling #4388reviewMHTML pages with a meta charset were decoded twice, turning non-ASCII text into mojibake.
    2. 02Split

      Cut text into chunks small enough to embed.

      Breaks:A cut lands inside a character. The chunk round-trips and embeds as garbage.

      • splitlintAudits any splitter for it
      • langchain4j #6091mergedThe sentence splitter returned nothing for text with no detectable sentence, so the hierarchical splitter dropped it.
      • langchain4j #6308reviewSplitting by UTF-16 code unit cut surrogate pairs in half, producing chunks that cannot be encoded as UTF-8.
    3. 03Index

      Represent content so it can be found: vectors, and structure where it exists.

      Breaks:Structure is flattened away, or a race corrupts the index without failing.

      • ragedASTs, call graph and vectors for code
    4. 04Retrieve

      Find the passages that answer the question.

      Breaks:A confident answer from the wrong source, and nobody measured how often.

    5. 05Act

      Agents call tools and change things.

      Breaks:The agent reports done without evidence, or the tool wiring quietly points elsewhere.

      • PlumbMakes coding agents prove they are done
      • mastra #20591mergedNine storage adapters declared @mastra/core peer floors older than the exports they import; installs passed, then died on import.
      • chainlit #3013reviewPer-user OAuth for HTTP MCP server connections.
      • hermes-workspace #735reviewThe MCP server list was fetched from the wrong endpoint on both the probe and data paths.

    Upstream

    GitHub API · 2026-10-07

    19 fixes merged. 10 of them never raised an error.

    Fixes sent to libraries I use. Each one arrives with a regression test that fails before the patch and passes after it, because that is the only “I fixed it” a maintainer can check in a minute. The list is generated from the GitHub API; closed-unmerged PRs are left out.

    Filter fixes by class of failure
    Upstream pull requests: 19 merged across 12 projects, 14 in review.
    axios★ 109k#11118mergedCrashOnce an interceptor's handlers array was set to null, every later request threw a TypeError: the length was read before the null check.
    caddy★ 77k#7922mergedWrong resultA proxy URL resolving to a port with no host (http://:80) was accepted, though the code meant to reject it.
    docling★ 68k#3985mergedCrashCSV conversion failed when the first quoted field spanned a newline: the dialect was sniffed from a truncated line.
    prometheus★ 66k#19324mergedCrashAWS service discovery panicked on Lightsail instances whose optional fields were absent.
    prometheus★ 66k#19396mergedCrashThe same nil-dereference class in ECS task discovery, found by reading the code around the first fix.
    prometheus★ 66k#19486mergedPerformanceThe 14 discovery packages that still ran their tests serially now run them in parallel, checked for shared state first.
    rclone★ 60k#9785mergedSilent lossBox: a source that ended early was uploaded truncated and reported as a success.
    rclone★ 60k#9786mergedSilent lossYandex: the same truncated-upload-reported-as-success bug on a second backend.
    rclone★ 60k#9801mergedSilent lossHuawei Drive: the same truncated-upload bug on a third backend.
    rclone★ 60k#9986mergedCrashValid durations such as 1d12h were rejected by the option parser.
    rclone★ 60k#9987mergedWrong resultA --bwlimit timetable written out of week order applied the wrong limit before the first slot of the week.
    keycloak★ 37k#51356mergedWrong resultCredential-issuer metadata carried empty-string key attestation values that the OID4VCI spec forbids.
    mastra★ 29k#20591mergedCrashNine storage adapters declared @mastra/core peer floors older than the exports they import; installs passed, then died on import.
    goreleaser★ 16k#6752mergedWrong resultEvery generated Homebrew Cask failed brew style: stanza order came from comparing arch strings alphabetically.
    unstructured★ 16k#4449mergedPerformancespaCy ran three times on the same text per element: 88% of partition_html() time in the profile. Now once.
    unstructured★ 16k#4502mergedSilent lossDefinition lists were discarded from HTML, so a Sphinx API reference page partitioned to its headings and nothing else.
    langchain4j★ 13k#6091mergedSilent lossThe sentence splitter returned nothing for text with no detectable sentence, so the hierarchical splitter dropped it.
    go-git★ 7.8k#2305mergedCrashPush aborted if any tree anywhere in history held a control character in a path, even one deleted years earlier.
    trafilatura★ 6.9k#940mergedSilent lossText directly after a list, table, quote or code block was dropped from every output format.
    docling★ 68k#4388reviewSilent lossMHTML pages with a meta charset were decoded twice, turning non-ASCII text into mojibake.
    docling★ 68k#4391reviewSilent lossAsciiDoc: a titled paragraph and the text after it were dropped.
    docling★ 68k#4394reviewWrong resultAsciiDoc comments were emitted into the document as body text.
    prometheus★ 66k#19501reviewCrashRDS discovery dereferenced an optional cluster ARN; a response without it took the process down.
    rclone★ 60k#9787reviewSilent lossGofile and Google Photos: the truncated-upload bug on two more backends.
    rclone★ 60k#9985reviewWrong resultA size passed to the rc API as a JSON number was read as KiB, not bytes: 44040192 asked for 42 MiB and set 42 GiB.
    cobra★ 45k#2473reviewWrong resultHelp text alignment depended on the order commands were attached; cached path lengths went stale on re-parenting.
    unstructured★ 16k#4451reviewSilent loss<details>/<summary> content was discarded from HTML.
    unstructured★ 16k#4503reviewSilent loss<figure> content was discarded from HTML.
    thanos★ 14k#8976reviewCrashRemote-write v2 label references were not bounds-checked, so a malformed client payload panicked the request.
    langchain4j★ 13k#6308reviewSilent lossSplitting by UTF-16 code unit cut surrogate pairs in half, producing chunks that cannot be encoded as UTF-8.
    chainlit★ 13k#3013reviewFeaturePer-user OAuth for HTTP MCP server connections.
    go-git★ 7.8k#2359reviewWrong resultA subsection name containing a newline was written raw, producing a config file neither go-git nor git can read back.
    hermes-workspace★ 6.7k#735reviewWrong resultThe MCP server list was fetched from the wrong endpoint on both the probe and data paths.
    loading index…Full retrieval trace →