Diagrams and code analysis: drawing designs and reading repositories
How a diagram editor stores, draws, lays out and exports a graph; how to read GitHub, GitLab and Azure DevOps repositories through their APIs with read-only tokens, ETags and rate limits; how manifests become a service map without running anything; and how drift and threat rules run on a graph - step by step, with examples.
In short
A design tool and a code reader are two halves of one idea: a system is a graph of parts and connections. A diagram editor keeps that graph as data, draws it with SVG and HTML, places it with a layout engine and exports it as SVG, PNG or PDF. A code reader fetches a repository's manifests - compose files, Helm charts, package files - through the provider's REST API and turns them into the same kind of graph by static analysis. Put the two graphs side by side and you find drift; run rules over one and you have a threat model.
After this lesson you can Choose between SVG and canvas for a diagram editor and say why; lay out a graph automatically; read a repository politely with ETags and rate limits; turn manifests into a service map and run drift and threat rules on a graph.
Words you need
- Graph model A diagram stored as nodes (parts) and edges (connections), each with a stable id. A position is just one more field of a node.
- SVG Scalable Vector Graphics: shapes as XML elements in the page. Each shape can be clicked, styled with CSS and read by a screen reader, and stays sharp at any zoom.
- Canvas An HTML element you paint pixels on with JavaScript. Very fast for huge drawings, but the page no longer knows which pixel belongs to which shape.
- Photographing the DOM Making a picture of part of a live page by cloning its elements with the styles they really have, wrapping the clone in an SVG foreignObject and letting the browser draw it. html-to-image does this.
- Layered layout A layout engine (ELK, Dagre, Graphviz) places nodes and routes edges for you. A layered layout is the Sugiyama method: nodes go in layers along the direction of flow, then each layer is ordered so few lines cross.
- Personal access token A key a person creates for an API instead of a password, limited by scopes such as read-only and often by an end date.
- ETag A short fingerprint a server sends with an answer. Send it back in If-None-Match and the server can answer 304 Not Modified with no body.
- Rate limit How many requests a provider allows in a time window. Past it you get 429 (or 403 on GitHub) and a time to wait.
- Webhook and HMAC A webhook is an HTTP call the provider makes to you when something happens, such as a push. It carries a signature: an HMAC is a fingerprint computed from the message and a shared secret, so if your recomputed one matches, the sender knew the secret and nothing changed on the way.
- Manifest A file that declares how software is built or run: docker-compose.yml, a Helm chart, a Kubernetes manifest, package.json, a .csproj.
- Drift The gap between what a model says and what is really built or running.
- Threat model A structured look for what could go wrong in a design, often with the STRIDE checklist, done on a graph of data flows and trust boundaries.
How a diagram editor works inside
Every serious diagram editor - draw.io, Figma, Miro, Lucidchart - keeps three things apart: the document (the data), the view (what is drawn) and the actions (what a person does). Keep them apart and undo, saving, export and checks all become simple, because they all read one document.
- 1. The document is the truth A JSON graph of nodes and edges. Everything on screen is computed from it; nothing on screen is the truth.
- 2. The view draws it A rendering library turns each node into a shape. In {{brand}} it is React Flow: each node is an HTML element, the connections are SVG paths in a layer underneath.
- 3. Gestures become actions A drag, a resize or a new connection is reported as an action - move node api to (240, 80) - never as a direct edit of the drawing.
- 4. A reducer applies the action One pure function takes the document and an action and returns the next document. The canvas, the outline, the inspector and the checks all see the same change.
- 5. History records it Undo keeps patches - what changed and how to put it back - rather than whole copies. Every frame of one drag joins one step, so undo takes back the gesture.
- 6. Saving sends the document Autosave sends the whole document with the version it was based on. If someone else saved first, the server answers 409 Conflict instead of overwriting, and the editor reloads or merges. This is optimistic concurrency: no locks, and nobody silently loses work.
Why patches, not snapshots A snapshot per step copies the whole design every time someone nudges a box. A patch says only nodes, 0, x: 240 - tiny, exact, and easy to merge: {{brand}} joins all the patches of one drag or one burst of arrow-key presses into a single undo step.
SVG, HTML or canvas: how the shapes are drawn
The rule of thumb: retained mode (SVG or HTML - the browser remembers every shape) is best up to a few thousand shapes and gives you events, CSS and accessibility for free. Immediate mode (canvas or WebGL - you repaint everything yourself) wins only when the drawing is huge. An architecture or a workflow has tens to a few hundred parts, so retained mode is the right tool.
Keeping an SVG editor fast Rebuild a node's shape only when that node changed - with immutable data this is a cheap reference check - and load the heavy parts only when they are needed. {{brand}} downloads the editor when a design is opened, the layout engine on the first auto-layout and the picture exporter on the first export.
A diagram is data: the graph format
A good diagram format is self-describing and versioned: it names its format and version, gives every element a stable id, and says what each element is - a kind such as service or database - rather than how it looks. Colours and shapes are decided when it is drawn, so the same document can be shown on a canvas, printed in a PDF or written as Mermaid.
- Stable ids: edges point at ids, comments and references point at ids, and a comparison matches elements by id. Never use a label as an identity.
- Parents for groups: a node inside a group names the group it sits in (inside), and its position is relative to that group, so moving the group moves everything inside it.
- Types, not pictures: type datastore is a fact a checker can use; a cylinder icon is only a picture of it.
- Limits: a sane maximum of nodes, edges and characters, checked before anything is built.
- Check the format Read the format name and version first. An unknown format is refused with a clear message.
- Upgrade step by step Older versions go through a chain of small upgrades - v1 to v2, v2 to v3 - so each change is written once. A format with only one version so far has an empty chain.
- Normalize Fill defaults, drop unknown kinds, keep only edges whose two ends exist.
- Validate on the server The browser repairs leniently so nobody loses a drawing; the server checks strictly - unique ids, existing ends, size limits - because a request can come from anywhere.
An imported file is hostile input A file someone hands you can be crafted. Refuse XML document type declarations (they can pull in other files or expand into huge text), put a cap on how far a compressed page may inflate, and count nodes, edges and characters before building anything.
Automatic layout: how a graph places itself
Placing boxes by hand is fine for ten parts. For a design drawn from code, imported from a file or written by AI, a layout engine places them. Systems and workflows have a direction - a request flows from the browser to the API to the database - so the usual choice is a layered layout, described by Sugiyama, Tagawa and Toda in 1981.
- Break cycles Turn a few edges round for now (break the cycles), so the graph has no loops. They are turned back at the end.
- Assign layers Give each node a layer so every edge points forward: the web app in layer 1, the API in layer 2, the database in layer 3.
- Reduce crossings Reorder the nodes inside each layer so as few lines cross as possible. Doing this perfectly is NP-hard, so engines sweep layer by layer with heuristics such as the barycenter method.
- Place coordinates Give every node its x and y, keeping edges short and straight - ELK can use network simplex for this step.
- Route edges Draw each connection around the boxes (edge routing), with bends where needed. An editor may also keep only the positions and draw its own lines.
- Groups as compound nodes: a network zone holds its parts; the engine lays out inside it and sizes it to fit what it holds.
- Direction by meaning: {{brand}} lays out an architecture left to right and a workflow top to bottom, the way it is walked.
- Off the main thread: run the engine in a Web Worker, so a large layout never freezes the page. Answer each request by an id and restart the worker if it crashes.
- Real sizes: give the engine each box's measured width and height, or labels will overlap.
- One undoable change: a person should be able to take back a whole auto-layout with one undo.
Export: SVG, PNG, PDF and diagram as code
There are two ways to make a picture of a diagram. Photograph the view: copy what is on screen, in the person's own theme. Or draw from the data: build a fresh SVG from the document alone, in a fixed print palette - which also works on a server with no browser. {{brand}} uses both, each where it fits.
- Frame the whole design Find the bounds of every node, add a margin, and compute the zoom and offset that fit them all - not just the part that is in view.
- Copy the view as SVG A library such as html-to-image clones the live page elements into an SVG foreignObject, leaving out the minimap and the buttons.
- Rasterize for PNG Draw that SVG onto a canvas at twice the size and read it back as PNG, so it stays sharp on high-density screens and in slides.
- Draw from the data for documents For PDF and Word, an SVG is built from the saved document, every text escaped. The PDF keeps it as vectors; Word gets a PNG drawn once, with its size capped.
- Write it as code Mermaid text for wikis and pull requests: groups become subgraphs, each kind gets a shape, and every label is escaped so the text reads back.
The first half of that journey - copying a live page into an SVG - is what html-to-image does. A browser cannot hand you a screenshot of its own elements, so the library builds one. It clones the chosen element deeply and copies the computed style of every clone, so the copy looks right outside the page's stylesheets. It inlines what the picture would otherwise fetch later: images become data addresses and web fonts are embedded. It wraps the clone in an SVG whose foreignObject holds ordinary HTML, which the browser knows how to draw. toSvg returns that SVG; toPng loads it as an image, paints it on a canvas at the pixel ratio you ask for and reads the canvas back. Options let you set the background, the exact size, a style override such as a zoom transform, and a filter that leaves chosen elements out.
- html-to-image Clone, inline, wrap in an SVG foreignObject.; The browser's own engine draws the result, so modern CSS looks right; Gives SVG as well as PNG; Small, and easy to load only when an export is asked for; The SVG holds HTML, which many vector tools cannot open; Cross-origin images and fonts need CORS or they are skipped
- html2canvas Re-implements CSS painting on a canvas.; No foreignObject, so it works where that is poor; Repaints with its own engine, so newer CSS can differ; PNG only
- Screen capture API The browser records the tab or screen.; Pixel-exact, no cloning; Asks the person for permission each time; Captures only what is visible, not a whole zoomed-out design
- A server-side headless browser Render the saved data in Chromium and screenshot it.; Identical output for everyone; works from a job with no screen; A whole browser to run on the server; Needs the data to be drawable there, and costs server time
Know what the photograph is made of html-to-image's SVG is HTML in a foreignObject: browsers draw it, but Word, many editors and PDF tools may show it empty. Very large canvases at 2x can also exceed the browser's canvas size limit, and Safari has needed extra care with images and fonts. For documents, draw an SVG from the data instead, as described above.
Escape every label A label such as <script> or A & B written into SVG or XML either breaks the file or runs code when someone opens it in a browser. Escape & < > " and ' in every text you put into markup, and never fetch an image or a font that an imported file names.
The options in the market
- React Flow (xyflow) Nodes are React components (HTML); edges are SVG.; Nodes are ordinary React: icons, badges, wrapped text, form fields; Pan, zoom, selection, handles and minimap built in; MIT licence, actively developed; Slows down past a few thousand nodes; No layout engine: you bring one
- maxGraph / mxGraph The SVG engine behind draw.io.; Mature, with a large shape library; Layouts included; Its own object model, awkward inside React; mxGraph itself is no longer maintained; maxGraph is its successor
- JointJS SVG diagrams, open core with a paid edition.; Strong ports and links model; The best tools are in the paid edition; Another object model to learn
- GoJS A commercial canvas diagram library.; Very fast; Many layouts and tools included; Paid licence; Canvas-based, so hit-testing, accessibility and crisp vector output need extra work
- Hand-written SVG or canvas Build it yourself.; Full control; No dependency; Selection, zoom, handles, touch, keyboard and undo take months to get right
- ELK (elkjs) The Eclipse Layout Kernel, compiled to JavaScript.; Layered layout with groups inside groups; Many options; runs in a Web Worker; A large download: load it only when used; Many options to learn
- Dagre A small layered layout for JavaScript.; Small and simple; Good for flat flowcharts; Limited support for groups inside groups; Less active development
- Graphviz (dot) through WebAssembly The classic layout tool.; Excellent layered results; Many output formats; A large WebAssembly file; Hard to respect positions a person chose
- Force-directed (for example d3-force) Nodes push apart, edges pull together.; A natural look for networks with no direction; No sense of flow; A different result each run unless seeded
What {{brand}} chose, and why
{{brand}} draws with React Flow because a part on the canvas is more than a box: it shows its kind, technology, inputs and badges for live health, threats and coverage. Those are ordinary React components, so they reuse the app's design system, translations and accessibility rules. Architecture diagrams stay well inside what SVG and HTML handle.
- Drawing: React Flow A part is an ordinary React component, so it reuses the app's design system, translations and accessibility rules.
- Layout: ELK in a worker Designs have direction and nested boundaries, which ELK handles as compound nodes; a Web Worker keeps the page responsive.
- Pictures: both ways, on purpose html-to-image copies the canvas so the picture looks exactly like the screen. A fresh SVG drawn from the saved document goes into PDF as vectors (with QuestPDF) and into Word as a PNG, with no browser on the server.
- Reading code: the provider's REST API {{brand}} uses a read-only token because the API fetches only the manifests needed, pins them to a commit and can be cached. Teams that share no token run a CLI scan in their own pipeline.
- Findings: fixed rules A rule gives the same answer every time and can be tested; AI may only reword a finding, never add or remove one.
Reading a repository through its API
To compare a design with code you need some of the code's files. You could clone the repository, but that copies everything - history, binaries, files nobody needs - and needs a disk and a git binary. Every big provider also has a REST API: ask for the default branch, the head commit, the list of files at that commit, and then only the few files you want.
- 1. Check the token Call the repository once: learn its default branch and, where the provider tells, what the token may do. Refuse a token that can write - a reader never needs it.
- 2. Get the head commit Ask for the branch's latest commit id only. This is the call you repeat most, so make it conditional with an ETag.
- 3. List the files at that commit One recursive tree call (GitHub, Azure DevOps) or a paged one (GitLab). Pin every later read to this commit id, not the branch name, so the files cannot change halfway.
- 4. Pick the files Keep only manifests, chosen by name. Skip credential and key files before opening a single one.
- 5. Skip what did not change Hash every kept path with its blob id from the tree listing. If the hash equals last time, stop: nothing that describes services changed, and no file was downloaded.
- 6. Read with caps Fetch each file raw, with a size cap per file and in total. A file that is not UTF-8 text is reported, not read.
- 7. Analyse, then forget Turn the files into a structure of names, keep the structure and throw the files away.
Never let a token wander A host can answer with a redirect. Follow redirects yourself: every hop must be https, on a host you allow, and resolve to a public address - otherwise a crafted repository address can make your server call its own private network (server-side request forgery). Send the token only to the origin it was created for, and never write it into a log, an error or an answer.
Ask for the narrowest token Prefer a token limited to read access on just the repositories you need. On some providers an older token kind can read private repositories only with a scope that also allows writing, and some providers cannot report a token's rights at all - so tell people which kind to create.
- Clone the repository A shallow git clone.; Every file, from any host, over one protocol; Downloads far more than needed; Needs disk space, a git binary and clean-up; Hooks, symbolic links and huge files to guard against
- The provider's REST API Ask for exactly the files you need.; Only the files you ask for; Pinned to a commit and cacheable with ETags; Read-only tokens with scopes; Three dialects to learn; Rate limits
- GitHub's GraphQL API Several files in one query.; Fewer round trips; GitHub only; Cost-based limits are harder to predict
- Scan in the customer's own pipeline A command-line tool reads the files where they are and sends only the result.; The code never leaves their machine; Works with any git host; Needs a step in their pipeline
- An installed app (GitHub App, OAuth app) The provider issues short-lived tokens.; Fine-grained, short-lived tokens; Higher rate limits; An app to register and review with each provider - and again for each self-hosted server
{{brand}} offers the two marked paths. A connected repository is read through the provider's API with a read-only access token, which works the same on GitHub, GitLab and Azure DevOps, in the cloud or self-hosted. Teams that would rather share no token run the CLI's scan command in their own pipeline; its dry run prints exactly what would be sent, and sends nothing.
Being polite: ETags, conditional requests and rate limits
A design follows a repository for months, and most checks find nothing new. Two habits make those checks almost free for you and for the provider: conditional requests and respecting rate limits.
- First ask GET the head of the branch. The answer carries ETag: "8f2c1e0b7d". Keep the tag and the small body.
- Next ask Send the same request with If-None-Match: "8f2c1e0b7d".
- Nothing changed The server answers 304 Not Modified with no body; use the body you kept. On GitHub, an authorized request answered 304 does not count against the primary rate limit.
- Something changed 200 with a new body and a new ETag. Replace what you kept and go on to read the tree.
- Read the headers: GitHub sends x-ratelimit-remaining and x-ratelimit-reset (a Unix time); GitLab sends RateLimit-Remaining and RateLimit-Reset; any of them may send Retry-After, in seconds or as a date.
- Know the codes: 429 Too Many Requests everywhere, and on GitHub also 403 with remaining 0 or a Retry-After. A 403 without them means no access, not slow down.
- Wait once, briefly, then stop: if the host asks for a short pause, wait it out once; if it asks for longer, end this read and try again later rather than holding a worker.
- Back off exponentially: after each failure wait about twice as long as before, up to a cap, and never less than the host asked.
- Key the cache safely: never use the raw token as a cache key or write it to a log. Key by a derived value and give the cache a size bound.
- Share fairly: if one worker reads for many customers, serve them in turn so one busy repository cannot starve the rest.
Three cheap questions before an expensive one Did the head move (a 304 says no)? Did any manifest's blob id change (a digest says no)? Only then download files. Most polls stop at the first question. A push that touches only source code is dropped at once when the webhook lists its changed paths, and stops at the second question when it does not.
Push or poll: webhooks and their proofs
Polling keeps asking anything new? A webhook turns that round: the provider calls you when something happens - a push, a merged pull request. It is faster and cheaper, but the address is public, so every call must prove it came from the provider before you believe a word of it.
The proofs below rest on an HMAC: a fingerprint computed from the message and a secret that only you and the provider know. You recompute it over the body you received. If it equals the one in the header, the sender knew the secret and the body was not changed on the way. Anyone without the secret cannot produce a matching fingerprint.
- Cap the size Refuse a body larger than any real event before reading it.
- Prove first Compute the expected proof and compare in constant time, so nobody can learn the secret from how fast a wrong guess fails. Give an unknown address and a wrong proof the same plain answer.
- Limit the rate Cap deliveries per source: a normal stream is small, and a flood is turned away.
- Drop replays Remember recent deliveries; the same one again is a replay and changes nothing.
- Take only what you need From a push, take the branch and the changed paths. If no manifest changed, do nothing. Never treat the payload as the code: read the head from the repository itself.
- Queue, coalesced Ten pushes in a minute become one waiting reading of the newest commit.
- Poll as a fallback If a webhook stays quiet for a long time, check the head now and then, conditionally.
Sign over the raw bytes Check the signature over the body exactly as it arrived. If your framework parses and re-serializes the JSON first, spacing or key order changes and every honest delivery fails - or, worse, you start skipping the check.
Static analysis: a service map from manifests
Static analysis learns about software without running it. Source code is hard to read reliably across dozens of languages, but manifests are short, structured and say exactly what a system is made of: which services run, what they are built from, what they depend on and which drivers they load.
- Collect List the files git tracks, or the provider's tree. Skip secrets by name before opening anything.
- Parse safely Read YAML, JSON and XML with bounded depth and size, refuse XML document types, and count what YAML aliases would expand to - a few kilobytes can otherwise become gigabytes (a YAML bomb).
- Extract facts Each analyzer turns one kind of file into small facts: service orders, image postgres:16, depends on orders-db, read at line 12.
- Merge by name Facts that name the same service meet in one node. When files disagree, a fixed priority order decides, and ties break in a fixed order, so the same files always give the same map.
- Infer connections depends_on and chart dependencies are edges. A setting whose host equals a scanned service's name is an edge. A driver package says the service talks to that kind of store.
- Write names only Output services, connections and endpoints, each with the file and line it came from - never a value, a port or an environment variable.
Never run what you analyse Do not call npm install, dotnet restore or helm template to understand a repository: they run the repository's own scripts on your machine. Read files as text. A Helm template full of {{ }} expressions is marked as read in part, not rendered.
- Skip credential and key files by name before reading anything.
- Look at a value only to find a host, compare it with the scanned service names in memory, and drop it.
- Check every name that leaves your machine for anything that looks like a secret.
- Offer a dry run that prints exactly what would be sent, so a security team can read it before anything leaves.
Detecting drift: diffing a model against reality
Drift happens to every diagram: the system changes and the drawing does not. Finding it is a graph diff between the model and the evidence - a code reading, or what API tests saw the running service answer. The hard parts are identity and honesty: knowing which box is which service, and not calling something missing when you simply have not seen it.
- Normalize both sides Give the same thing the same shape: method in capitals, path in lower case, every path parameter alike - so /orders/{orderId} and /orders/{id} are one endpoint.
- Join by stable keys A component knows its code by deterministic links - one a person made, or the id a node was drawn with from the code. A similar name is only a suggestion a person confirms.
- Compare what each side says Same technology? PostgreSQL agrees with PostgreSQL 16, but .NET 8 does not agree with .NET 10. Same connection kind? Does the code have every endpoint the design names?
- Claim only what the evidence proves Say something is missing only when there is a code reading. Runtime evidence alone cannot prove absence, so the honest result is "not seen live".
- Find what nobody drew Services, connections and running APIs in the evidence but not in the model are listed as not in the model.
- Remember decisions Keep a person's decision outside the model, together with a hash of the evidence it was made on, and re-open it when the evidence changes.
- Score it Matches divided by everything counted. Endpoints are details of a component, so a 200-endpoint API cannot swamp the score.
A move is not a change Compare what a design says, not where a box sits. Leave positions and sizes out of every diff and fingerprint, or each tidy-up looks like a redesign and reopens every review.
The same idea works one level down. A workflow step that designs POST /orders answering 201 is compared with the API's contract and with what test runs observed: Running POST /orders answers 202, the design says 201. Keep the decision about each difference beside the evidence, not inside the design.
Rules on a graph: threat modelling and architecture checks
Once a design is a graph, many review questions become graph queries. Threat modelling asks what could go wrong where data crosses a trust boundary; the classic checklist is STRIDE - spoofing, tampering, repudiation, information disclosure, denial of service and elevation of privilege. Tools such as the Microsoft Threat Modeling Tool and OWASP Threat Dragon run rules like these over data-flow diagrams.
- Pre-compute context Walk each node's parents once to know every zone it sits in, however deeply nested.
- Match whole words Split labels into words and compare whole words. The word tls then matches the label "TLS over TCP", but a longer word that merely contains those letters does not.
- One finding, one stable key Key a finding by its rule and the element it is about, for example no-tls-crossing:link-7, so an accepted risk stays accepted while that element exists.
- Same input, same output Sort findings by severity, then rule, then name, so the list never jumps around and tests can pin it.
- Bound the work Cap how many findings there can be and how deep a walk goes, so a hostile design cannot keep the server busy.
- Explain, then let people decide Give each finding a reason and a suggested fix, and store an accepted risk beside the model with its reason and author.
- Fixed rules Deterministic checks over the graph.; The same answer every time; Explainable: each finding names its rule and elements; Free to run and easy to test; Finds only what the rules know; Depends on the words people wrote
- AI judging the design A model reads the design and lists risks.; May notice an unusual risk; Different answers on different days; Can invent risks or miss real ones; Costs per call and is hard to test
- A security review by people An expert walks the design.; Deep and in context; Slow, rare and depends on the reviewer
{{brand}} decides findings with fixed rules only; AI may word the findings in plain language but never adds, removes or changes one. The same engine style - pure functions over the graph, stable keys, a fixed order - also runs the editor's architecture checks, such as parts nothing connects to and rings of synchronous calls.
Try it yourself: map a repository in an afternoon
You need Node.js and a public GitHub repository with a compose file. No token is needed for public repositories, but the rate limit without one is much lower, so run it a few times only.
- Pick a repository Any public project with a docker-compose.yml, for example a sample shop.
- Get the tree Ask the GitHub API for the default branch and its recursive tree.
- Find the manifest Keep only the compose file, found by name.
- Parse it Read services and depends_on with a YAML library.
- Draw it Print Mermaid - flowchart LR and one line per dependency - and paste it into any Mermaid viewer.
- Make it polite Run the tree call twice. The second time send the ETag you got in If-None-Match and watch for 304 Not Modified (code below).
- Compare the budget Print x-ratelimit-remaining after each call. The 304 answer should not use up the budget.
- Install Run npm install immer elkjs in an empty folder.
- Try undo Run the Immer snippet from earlier and print the patches and the inverse.
- Try layout Run the elkjs snippet and print the x and y of each child.
- Change a size Make one node wider and run the layout again. Watch the others move.
Common mistakes
- Matching by name: Orders API and orders-service are probably the same, but a guess must be a suggestion a person confirms, or the score quietly lies.
- Calling absence a failure: no evidence is not negative evidence. Say not seen; say missing only when a full reading lacks it.
- Positions in the diff: tidying a diagram must never count as a change or reopen a review.
- Trusting a webhook before checking it: verify the signature over the raw body first, in constant time; parse afterwards.
- Asking for a write token: a reader only reads. Refuse a token the provider says can write, and tell people which scope to choose where it cannot tell.
- Running the repository to understand it: npm install or helm template executes someone else's code on your server.
Interview questions
- SVG or canvas for a diagram editor? SVG and HTML for up to a few thousand shapes: you get events, CSS, accessibility and vector export for free. Canvas or WebGL only when the drawing is huge, because then you must build hit-testing, text and accessibility yourself.
- What is a trust boundary, and what is STRIDE? A trust boundary is where data passes between parts with different levels of trust, such as the internet and a private network. STRIDE is a checklist of six threat types - spoofing, tampering, repudiation, information disclosure, denial of service, elevation of privilege - that you apply to every crossing.
- How do you implement undo in an editor? Make every change an action applied by a pure reducer, and record patches with their inverses, grouping the frames of one gesture into one step. Undo applies the inverse; redo applies the patches; a new action clears the redo list.
- Webhook or polling, and why not just one? Webhooks are fast and cheap but arrive on a public address, so verify the signature, drop replays and keep a slow conditional poll as a safety net. Polling alone is simple but late and wasteful.
- What is an ETag, and why use conditional requests? An ETag fingerprints a response. Sending it back in If-None-Match lets the server answer 304 with no body. On GitHub an authorized 304 does not count against the primary rate limit, so frequent polling is almost free.
- How do you handle a provider's rate limit? Read the remaining and reset headers and Retry-After. Treat 429, and GitHub's 403 with remaining 0, as slow down. Wait once if the pause is short; otherwise stop and retry later with exponential backoff, sharing capacity fairly between customers.
- How do you verify a webhook? Compute the provider's proof - for GitHub an HMAC-SHA256 of the raw body - and compare it in constant time before parsing anything. Then rate-limit, drop replays and coalesce many pushes into one job.
- How do you map a system's services without running its code? Statically analyse manifests: compose, Helm, Kubernetes, Dockerfiles, project files and OpenAPI. Parse with depth, size and alias limits, merge facts by name with priorities, infer edges from dependencies and host names, and keep names with file and line - never values.
- How do you detect drift between a diagram and the code? Normalize both sides, join elements by deterministic keys rather than names, compare technology, connection kinds and endpoints, and only call something missing when a full code reading lacks it. Remember decisions with a hash of the evidence so they reopen when it changes.
- How would you build a threat-model checker? Model the design as a graph with trust boundaries. Write each rule as a deterministic graph query with a stable key per finding. Sort the output, bound the work, and store an accepted risk with its reason.
Related
- /help/technology/frontend
- /help/technology/http-api-testing
- /help/technology/secure-links-privacy