← Portfolio · Article · Algorithms
Algorithms I used and why
I don't invent algorithms: I pick well-known techniques that solve a concrete problem, adapt them and measure them. Here are the ones from my projects, with the problem they solved, why I chose that one and not another, and the real code.
01Matching names across sources: Jaro-Winkler
Congreso Abierto
Each official source writes the same person differently: «Chiconi, Abel» in the roster and «CHICONI RICARDO ABEL» in the travel records; «Herrera Ahuad, Oscar A.» versus «HERRERA AHUAD OSCAR ALBERTO». Without matching, trips, committees and votes end up disconnected.
What I chose and why
I normalize the names (no accents, lowercase, no particles like «de» or «la») and compare word by word with Jaro-Winkler, a similarity measure that rewards matching prefixes, which is exactly what happens with truncated names. Each candidate gets a confidence score: the surname counts double, an initial like «A.» can stand for «Alberto», and a match is only assigned if it scores above 0.9 and beats the runner-up by 0.05. If two deputies tie (two «Torres»), the case is flagged as uncertain and shown; it is never assigned blindly.
src/lib/linkage.tsView on GitHub export function jaroWinkler(a: string, b: string): number {
if (a === b) return 1;
if (!a || !b) return 0;
const window = Math.max(0, Math.floor(Math.max(a.length, b.length) / 2) - 1);
const aHits = new Array<boolean>(a.length).fill(false);
const bHits = new Array<boolean>(b.length).fill(false);
let matches = 0;
for (let i = 0; i < a.length; i++) {
const end = Math.min(i + window + 1, b.length);
for (let j = Math.max(0, i - window); j < end; j++) {
if (bHits[j] || a[i] !== b[j]) continue;
aHits[i] = bHits[j] = true;
matches++;
break;
}
}
if (!matches) return 0;
let transpositions = 0;
for (let i = 0, j = 0; i < a.length; i++) {
if (!aHits[i]) continue;
while (!bHits[j]) j++;
if (a[i] !== b[j]) transpositions++;
j++;
}
const jaro = (matches / a.length + matches / b.length + (matches - transpositions / 2) / matches) / 3;
let prefix = 0;
while (prefix < 4 && prefix < a.length && prefix < b.length && a[prefix] === b[prefix]) prefix++;
return jaro + prefix * 0.1 * (1 - jaro);Result
In the November 3, 2026 build: committees 99.6 % matched (1,612 of 1,618), Chamber of Deputies bills 99.1 % (14,523 of 14,651) and Senate votes 100 % (24,912 of 24,912). The hard real-world cases were pinned as tests.
What I ruled out: exact text comparison (it fails on any middle name) and Levenshtein distance (it penalizes an error at the start the same as one at the end, and with truncated names the start matters more).
02Comparing fairly: robust statistics
Congreso Abierto
I wanted to show which deputies travel considerably more than their peers. With the mean, two or three extreme cases shift the whole bar, and comparing someone from Jujuy with someone from the City of Buenos Aires isn't fair.
What I chose and why
I use the median and the median absolute deviation (MAD), which aren't moved by a few extreme values, and the Iglewicz and Hoaglin modified z-score: anyone above 3.5 is flagged. Peers aren't defined by province but as deputies at a similar distance from Buenos Aires, estimated with the haversine formula over their usual routes. The page says «travels considerably more than their peers», never «abuses».
src/lib/viajes.tsView on GitHub /** Iglewicz-Hoaglin modified z-scores, with the mean-absolute-deviation fallback when MAD is 0. */
export function modifiedZScores(values: number[]): number[] {
const med = median(values);
const deviations = values.map(x => Math.abs(x - med));
const mad = median(deviations);
if (mad > 0) return values.map(x => (0.6745 * (x - med)) / mad);
const meanAd = deviations.reduce((t, d) => t + d, 0) / (deviations.length || 1);
if (meanAd > 0) return values.map(x => (x - med) / (1.253314 * meanAd));
return values.map(() => 0);
}Result
A comparison that can be explained and defended, with the methodology written on the site and the caveat that more trip legs don't imply an irregularity.
What I ruled out: grouping by region: Buenos Aires mixes the metro area and the interior, so region made the comparison unfair and I replaced it with distance bands.
03Fast lists with thousands of orders: cursor pagination
UNNA
A café closes hundreds of orders a week. With «page 2, 3, 4…» (OFFSET), the database scans everything before it on every page, and if an order changes status between two pages, a row gets skipped or repeated.
What I chose and why
I use cursor (keyset) pagination: each page asks for «the rows before this one», ordered by date and by id as a tiebreaker, backed by a composite index. The cursor is validated within the business, so a foreign cursor can never shape the query.
apps/api/src/business/adapters/keyset-page.ts · excerptView on GitHub // Keyset pagination of the business lists (S17), newest first by (createdAt, id).
// The page is "rows strictly before the cursor" in that order, instead of Prisma's
// `cursor` + `skip: 1`: that skip drops a valid row whenever the cursor row no longer
// matches the filters (e.g. an order that was closed between two pages of `open=true`).
export const afterCursor = (cursor: KeysetCursor) => ({
OR: [{ createdAt: { lt: cursor.createdAt } }, { createdAt: cursor.createdAt, id: { lt: cursor.id } }],
});Result
Measured with about 730 orders: the list went from 73 ms and 1 MB down to 4.5 ms and 28 KB.
What I ruled out: Prisma's skip, which loses a valid row in exactly the case that happens most in a café: an order that gets closed while you're looking at the list.
04Sequential numbering without duplicates: time-based leases
UNNA
ARCA only approves the next number for each point of sale. If two registers invoice at the same time, or if ARCA is slow, you have to avoid requesting the same number twice without leaving the database locked while waiting for the response.
What I chose and why
Each sequence has a lease with an expiry: the receipt that holds it is the only one allowed to call ARCA. The lease is taken in a short transaction with a row lock, the ARCA call happens outside any lock, and the result is saved in another short transaction. If the process crashes, the lease expires and another one picks it up.
apps/api/src/business/use-cases/receipt-issue-lease.tsView on GitHub if (await locked.sequence.leaseHolder(now)) return { kind: 'busy' };
await locked.sequence.claimLease(draft.id, new Date(now.getTime() + RECEIPT_ISSUE_LEASE_MS));
await locked.receipts.save(draft);
return { kind: 'leased', draft, lastNumber: await locked.sequence.lastNumber() };Result
Numbering stays sequential even with two registers at the same time, and if ARCA doesn't respond, the receipt stays as a draft that can be retried without burning the number.
What I ruled out: holding the row lock during the ARCA call: one slow response stalled every sale at that branch.
05Keeping an email from sounding like AI: stylometric analysis
laburo
Emails written by a model are instantly recognizable: sentences of the same length, filler phrases like «Además» or «Cabe destacar», all starting the same way.
What I chose and why
Before showing a draft, I measure it: the variation in sentence length (human writing mixes short and long sentences), typical AI filler phrases, repeated openings and mixed forms of address. Each problem costs points; below the minimum, the draft is regenerated along with the list of problems.
app/internal/draft/humanize.go · excerptPrivate repofunc Analyze(body string) Report {
r := Report{Score: 100}
ss := sentences(body)
if len(ss) >= 3 {
var lens []float64
for _, s := range ss {
lens = append(lens, float64(len(wordRe.FindAllString(s, -1))))
}
r.Burstiness = variation(lens)
if r.Burstiness < minBurstiness {
r.Score -= 20 // every sentence has about the same length
}
}
// ...AI connectors, repeated sentence starts, mixed tú/ustedes
return r
}Result
Every draft arrives with a naturalness score and the list of what needs fixing; the ones that fail are regenerated once before reaching the preview.
What I ruled out: asking the model to «not sound like AI» and trusting it: without a measure, there's no way to know whether it complied.
06Detecting repetitive storyboards: visual fingerprint
Mr.Mauroo Studio
A storyboard can score 10 on «variety» and still be boring: every frame with giant text on a flat background. I needed a measure that looked at the image, not the text.
What I chose and why
Each frame is reduced to a fingerprint: a 4×3 grid with how much of each zone isn't background, how much the background takes up and how many colors sit on top. Two frames «repeat» if the distance between their fingerprints is below 0.03 or if both are text only. The score drops one point for every 5 % of frames that repeat any of the previous four.
src/critic/domain/sameness.ts · excerptPrivate repoexport const SAMENESS = { window: 4, layoutAlike: 0.03, flatAlike: 0.1, bareFlat: 0.6, bareColors: 2, sharePerPoint: 0.05 };
const isBare = (f: Fingerprint) => f.flat >= SAMENESS.bareFlat && f.colors <= SAMENESS.bareColors;
const layoutDistance = (a: Fingerprint, b: Fingerprint) =>
a.layout.reduce((sum, v, i) => sum + Math.abs(v - (b.layout[i] ?? 0)), 0) / Math.max(1, a.layout.length);
const samePicture = (a: Fingerprint, b: Fingerprint) =>
layoutDistance(a, b) <= SAMENESS.layoutAlike && Math.abs(a.flat - b.flat) <= SAMENESS.flatAlike;Result
The storyboard that used to score 10 on variety now scores 1 on «repetition» (91 of 118 frames repeated) and the critic stops it; the 0.03 threshold comes from real measurements, where there was a clear gap between 0.018 and 0.037.
What I ruled out: comparing each frame's text: two frames with different sentences can look exactly the same.
07Labeling hundreds of seats: pattern expansion
SeatFlow Studio
A theater seating map has hundreds of seats, and naming them one by one is slow and error-prone.
What I chose and why
A small range parser expands patterns like A{1..10} or {A..C}{1..2} into all their combinations (a Cartesian product of the ranges), with numeric and letter ranges, ascending or descending, and a cap of 500 per range so the interface doesn't hang.
src/services/labeling/patternParser.tsView on GitHub /**
* Parses a pattern string and generates an array of labels.
* Examples:
* - "A{1..3}" -> ["A1", "A2", "A3"]
* - "{A..C}1" -> ["A1", "B1", "C1"]
* - "{A..B}{1..2}" -> ["A1", "A2", "B1", "B2"]
*/
export const parsePattern = (pattern: string): string[] => {Result
An entire theater row is labeled with a single line, and the result is validated before it's saved.
What I ruled out: a giant regular expression: nested patterns become unreadable and can't be tested piece by piece.
08Searching without decrypting: HMAC blind index
Healthtech platform (under NDA)
Sensitive data is stored encrypted with AES-256-GCM, which produces a different output every time. That's secure, but it makes it impossible to ask «does this national ID exist?» without decrypting the whole table.
What I chose and why
Next to the encrypted value I store a blind index: a deterministic HMAC-SHA256 of the normalized value, with its own separate key per field. Searching means comparing HMACs; the original value is never decrypted to search.
I don't show the code: the project is under a confidentiality agreement.
What I ruled out: deterministic encryption of the value itself, which exposes which records are equal and makes frequency attacks easier.
09How I choose an algorithm
- The problem first, with a real case. «Chiconi, Abel» versus «CHICONI RICARDO ABEL» is worth more than any definition.
- The simplest thing that can be measured. If a clear rule is enough, I don't use a model; if a measure is needed, I define it and test it with real data.
- Tests with the hard cases. The examples that motivated the algorithm are pinned as tests before any number is published.
- Show the uncertainty. What's uncertain gets reported, not hidden: uncertain matches in Congreso Abierto, uncertain sends in laburo.