Saturday, August 22, 2026

How to Answer Questions the Smart Way

For years, one of the top recommendations on my Required Reading list has been Eric S. Raymond's classic essay, How to Ask Questions the Smart Way.

I still recommend it. It is a foundational text on respecting other people's cognitive load. Do your homework, provide context, state the problem clearly, and make it easy for the person helping you.

But after a couple of decades working across architecture, operations, and data engineering, I've realized something. We spend a massive amount of time teaching engineers how to ask better questions. We spend almost zero time teaching experts how to answer them.

(I should pause here and say: I am not immune to this. While I struggle to even apply the label "expert" to myself, I know for a fact I have been guilty of exactly what I am about to describe. I have given the breadcrumb answers. There is plenty of room for humility here.)

But regardless of who is doing it, the truth remains: a lot of the communication failures I see aren't caused by bad questions. They are caused by bad answers.

The core problem is usually this: The asker is trying to establish the model. The responder is answering with implementation details, caveats, and breadcrumbs.

The Excavation

We have all witnessed, or participated in, this exact pattern:

Question: Does feature X do Y by default?
Answer: Well, it can be configured differently depending on the deployment.
Question: Right, but out of the box, does it do Y?
Answer: Administrators can change the setting to do Z instead.
Question: Okay, but if I just turn it on without changing anything, what happens?
Answer: Yes, it defaults to Y.

What follows is an excavation. The asker has to carefully dig through three or four rounds of follow-ups just to extract a simple fact.

If it takes twenty minutes and a half-dozen replies to get a one-sentence answer, the problem was not the question. The question was fine. The failure was in the information transfer.

The Expert's Burden

ESR's essay is fundamentally about reducing the cost imposed on the answerer. But there is a reciprocal obligation. If you are the expert, the owner, or the authority, you owe clarity to the asker.

I understand why this happens. It is usually a defensive mechanism. We front-load the caveats because we are terrified of being technically "wrong" or called out over some obscure edge case. We want to protect ourselves by dumping all our context onto the table at once.

But the obligation to respect someone else's time does not end when they finish asking the question.

A good answer reduces uncertainty. It shrinks the search space. A bad answer expands it.

If the audience still has the same question after you respond, you haven't helped them. You have just transferred your cognitive load onto them, forcing them to reconstruct your intent.

It is very similar to the problem with passive voice (something Cary Millsap has talked about for years). The real sin of passive voice isn't grammar. It is making the reader work to reconstruct causality.

Poor technical answers create the same problem. The audience must reconstruct the model, the assumptions, and the contract from fragments scattered across multiple replies.

Answer Like an API

Answering questions effectively is an architecture skill.

A systems person naturally thinks in contracts. What is the source of truth? What guarantees does the system make? What is the documented behavior? Everything else is just plumbing.

We need to treat our answers the exact same way. When someone asks a question, they are usually looking for the contract.

Experts often begin with caveats, history, edge cases, implementation details, and exceptions. Resist that urge.

The answer goes first. Everything else is commentary.

Question: Does feature X do Y by default?
Better Answer: Yes, it defaults to Y out of the box. The configuration option allows you to override this behavior. Here is the link to the doc.

Experts often answer in chronological order ("Here is the history, here are the caveats, therefore the answer is X"). Good communicators answer in logical order ("The answer is X, here is why, here are the caveats").

It is the Minto Principle applied to engineering. The answer should be the first sentence, not the last.

Reduce Ambiguity

The same instinct that drives us toward explicit schemas, API definitions, and data contracts should drive our communication. Make the model explicit. Put the definition where everyone can see it.

The purpose of an answer is not to display expertise. The purpose of an answer is to transfer understanding.

Good architecture, documentation, and APIs all do one thing: they reduce ambiguity. Good answers should do the exact same.

Don't make people pull teeth to understand the system.

Sunday, May 17, 2026

You Can Point a Foreign Key Where?!

Editor's Note: written entirely by Gemini (with minor edits by me)

Let’s talk about things we think we know, but it turns out we’ve (read: me) just been following muscle memory for twenty-plus years.

If you asked me on any given Tuesday what a foreign key does, I’d give you the standard textbook answer. It points to the primary key of a parent table. It’s bread-and-butter relational modeling. We back it with a sequence or an identity column, we join on the IDs, and we move on with our lives.

But a funny thing happened on the way to the database the other day. I realized, or rather, I was reminded, that the SQL standard and Oracle Database don’t actually care about your primary key.

A foreign key doesn't have to reference a PRIMARY KEY. It just needs to reference a minimal unique identifier. That means any column set with a valid UNIQUE constraint is fair game.



















The Setup

Imagine you have a standard reference lookup table for order statuses. You’ve got your surrogate auto-incrementing ID as the PK because that’s what we do. But you also have an alphanumeric business code that the application actually uses, and that code is guaranteed unique.


CREATE TABLE order_statuses (
    status_id   NUMBER GENERATED BY DEFAULT AS IDENTITY,
    status_code VARCHAR2(10) NOT NULL,
    description VARCHAR2(100) NOT NULL,
    --
    CONSTRAINT pk_order_statuses PRIMARY KEY (status_id),
    CONSTRAINT uq_order_statuses_code UNIQUE (status_code)
);
Normally, devs will map the status_id down to the child orders table. But what if you map the code instead?
CREATE TABLE orders (
    order_id     NUMBER GENERATED BY DEFAULT AS IDENTITY,
    order_status VARCHAR2(10) NOT NULL,
    -- Look Ma, no status_id!
    CONSTRAINT pk_orders PRIMARY KEY (order_id),
    CONSTRAINT fk_orders_status 
        FOREIGN KEY (order_status) 
        REFERENCES order_statuses (status_code)
);

This compiles. It validates. It works.

Why Do We Care?

If you are a "data-first" person, this opens up some interesting pragmatic design choices, especially for seed data and reference enums.

  1. No-Join Readability: When I run a quick SELECT * FROM orders, I don't see status 1, 2, or 3. I see 'PENDING', 'SHIPPED', or 'CANCELLED'. I don’t have to write an explicit JOIN to a lookup table just to debug a row in a terminal log.

  2. CI/CD Sanity: Moving seed data across Dev, QA, and Prod environments when you rely purely on surrogate sequences can be a nightmare of dynamic mapping scripts. Business codes are immutable constants across environments. Your deployment scripts can just hardcode the literals without breaking things.

The Fine Print (Because this is Oracle)

Before you go rewriting your entire data model, remember that the laws of physics still apply.

First, Oracle does not automatically index foreign keys. If you point a child table to a parent’s unique business code, and somebody tries to delete or modify that code in the parent table, Oracle has to scan the child table to ensure no orphan records are left behind. If you didn’t manually put an index on orders.order_status, you are looking at a Full Table Scan and a nasty shared sub-exclusive table lock (TM) that will freeze concurrent operations.

Second, don't try this on your Slowly Changing Dimension (SCD) Type 2 tables. The second a business code repeats because you are tracking historical versions with effective dates, table-wide uniqueness breaks. And no, you can't use a partial function-based index to bypass this; declarative foreign keys need real, concrete constraints.

Relational Reality Check

In relational theory, a foreign key references a candidate key, which is simply a minimal superkey. The choice to elevate one candidate key to be the "Primary Key" is a physical implementation choice, not a logical requirement.

It’s completely valid, ANSI-standard behavior. It’s supported in Postgres and SQL Server too, so it’s not just an Oracle quirk.

It's just one of those elegant database features hiding in plain sight while application layers spend thousands of lines of code trying to reinvent referential integrity.

Keep it in the database.

Appendix: Documentation & Structural Foundations

Oracle Database Documentation

  • Oracle SQL Language Reference — The constraint Clause: The definitive syntax rules and structural restrictions governing referential integrity, confirming that foreign keys can target primary keys or unique constraints.

    Oracle Database SQL Language Reference — Constraints

Edgar F. Codd & Relational Theory

  • The 1970 Foundation Paper: Codd, E. F. (1970). "A Relational Model of Data for Large Shared Data Banks." Communications of the ACM. The original blueprint that introduced relational algebra, establishing that relationships are derived strictly by matching domains over mathematical relations rather than rigidly named primary/foreign key pairs.

    ACM Digital Library — A Relational Model of Data for Large Shared Data Banks

  • The Relational Model for Database Management: Version 2 (Book): Codd, E. F. (1990). Addison-Wesley. Codd formalizes RM/V2, explicitly grouping Primary Keys and Alternate Keys together under the definition of Candidate Keys, proving that referential integrity mathematically depends on the candidate key property of uniqueness.

    ACM Digital Library — The Relational Model for Database Management (Version 2)

Tuesday, May 12, 2026

The System Works

For the last 20 years or so I've thought about what retirement might look like. 

In retirement, I'd go back to school and write a thesis on... I didn't know what to call it. The "Medical Industrial Complex?" Much of this was due to the many, many, many interactions as it relates to Kate

"I'll go back to school, do the research, get my PhD." Mostly so I could participate in Spies Like Us shenanigans: Doctor. Doctor. Doctor. Doctor.

I had this gut feeling that something was...broken; none of it made sense. 

When I didn't have insurance, $115 copay. When I did have insurance, $400. 

As a consultant/contractor, I understood how we measure things in time; n dollars/hour. 

OK, so Doc is $115 x 4 (because it may have been 15 minutes, I'm being super generous here), how is it now $400 x 4/hour? How in the hell does handing over a piece of plastic turn a $460/hr rate into $1,600/hour?

I'm (often) told I need a hobby. Outside of work. What many don't realize is that I'm one of the fortunate ones; what I do for a living is not work.

Choose a job you love, and you will never have to work a day in your life. Or whatever that phrase is. 

So, one Sunday, a few weeks back, I set Antigravity (Gemini) loose on my idea. I just told it: "I have an idea I want to explore."

I didn't expect a book. I expected a conversation. But what happened over the next few hours was the mechanical equivalent of defining the schema for 20 years of observations. I threw two decades of "gut feelings" at the machine, and it started mapping the plumbing. Before I knew it, I had a 23-bullet outline and links to 50-75 outside sources.

"Fully fleshed out, how long would this be?"

"350-400 pages."







"OK, let's pare that back"

Gemini proceeded to suggest serializing it on Substack or just going with an abbreviated manifesto.

I chose the manifesto. We pared it down to 8 dense chapters. It wrote the Intro. 

What...do I...OMG...this is...wow. 

If you spend enough time listening to patients, doctors, or politicians, you will hear a single, unifying complaint spoken across every demographic and tax bracket in America:

The healthcare system is entirely broken.

It is the one rare diagnosis found across every boardroom, breakroom, and waiting room in the country. They point to the soul-crushing administrative bloat that forces practitioners into eight-minute volume appointments. They point to the labyrinthine "prior authorization" processes where algorithms deny life-saving treatments.

And yet, this frustration is born entirely from a profound paradox.

If you strip away the billing departments and the insurance mandates, the actual clinical engineering of the American medical system is nothing short of miraculous. If you are diagnosed with a rare leukemia, or require an unprecedented cardiothoracic intervention, there is quite literally nowhere else on earth you would rather be. The United States produces the most advanced, precision-engineered medical science in the history of human civilization. The people executing that science—the physicians, the bio-engineers, the surgical staff—are elite.

That is not the original, but you get the idea. I spent time reading and editing that introduction, changing the tone, the focus, where I ended up creating a GUIDING_PRINCIPLES.md file to keep it inline:

  • Dissect the Maze, Don't Indict the Mice
  • Empathy for the Inheritor
  • No Emotional Accusations, Only Systemic Mechanics
  • Labels are "Pre-Written Baggage"
  • Bypass Over Bureacracy
Super cool. A few iterations later, I had an Intro. On to Chapter 1. Same process. Edit for tone and clarity, but otherwise let it loose. Chapter 2, same. After Chapter 2, I just let it rip. By the end of that Sunday session, maybe 5 hours, I had a 40 page "book."

Over the next couple of days, I'd spend 30-60 minutes creating cover and chapter art (with Gemini web).

On Wednesday I had reached my quota for the month, the $20/month plan would reset on 4/11 (Saturday). 

I was exploring publishing options, I've always wanted to be a published writer (as I type that, I realize there are north of 800 articles here, so...). 

But like that, that specific itch was gone. I did not revisit on Saturday. I did share with friends and colleagues. I solicited feedback. I began to incorporate feedback and also track who provided what feedback. 

Fast forward a month, and I came head to head with this system again. It was unpleasant to say the least. 

Now, however, I was armed with new tools. The system hadn’t changed, but I could finally see how it moved; where the paths were, and why people kept ending up in the same places.


Saturday, May 2, 2026

The Death of the API Barrier: From Jargon Intimidation to Result Sets and AI

If you were a database guy in the early 2000s, APIs didn’t exactly show up with a gift basket and a smile.

They showed up as these massive, scary-looking blocks of XML that people called SOAP. Or maybe it was a WSDL? Honestly, I could barely spell those acronyms, let alone tell you what they were supposed to do. I didn’t have a Computer Science degree, and looking at those files felt like I’d wandered into a high-level physics lecture by mistake.

I was brand new to IT, and the whole "web service" thing was just...intimidating. It felt like a club I didn't have the password for. I didn't know how they worked, I didn't know why people liked them, and I certainly didn't want to admit I was lost. So I did what anyone does when they’re staring at something that makes them feel out of their depth: I retreated to safety.

I stayed where things made sense. Tables. Sets. SQL and PL/SQL. Logic sitting right next to the data where it belongs. I could look at a table and understand it. I could write a query and get a result. The database was my safe harbor in a storm of jargon I didn't understand.

That bias stuck with me for a long time. 


REST Didn’t Fix the Mindset 

Fast forward a decade. SOAP was finally out of fashion (h/t to everyone who survived that era). REST and JSON were the new hotness. We were told this was "better." And structurally, sure, it was. 

A few years back, I poked around with a Strava app to see if I was just being a crank. Clean endpoints. JSON payloads. Reasonable docs. 

And it was still exhausting. 

Not because REST was inherently bad, but because the underlying mindset hadn't shifted an inch. I was still being handed "object-shaped" payloads and expected to navigate an object graph like a tourist without a map. Nested structures. Lists of things containing lists of other things. Manual parsing until my eyes bled. 

That wasn’t a data problem. It was an OO worldview leaking all over my integration boundary. I wasn’t getting result sets; I was getting objects pretending to be data. 


PL/SQL Was Already the API (We Just Forgot) 

Here’s the part that often gets missed: PL/SQL has always been the API

A PL/SQL procedure returning a ref cursor isn't some low-level implementation detail. It’s a contract. It’s the database saying, “Here is a defined shape of data. Consume it as a set.” 

That distinction matters. 

A result set is declarative. It’s complete. It has no behavior, no lifecycle, no implied navigation path. It just is. Objects, on the other hand, carry all this baggage about how they want to be used. They imply traversal, ownership, and state transitions. 

One is about truth. The other is about interaction. Most APIs were designed by app devs, so they expose objects. Not data. 

It's probably why I loved APEX so much. It didn't ask me to pretend my data was a "user object," it just asked me for the query.


AI Is the New Translation Layer 

What finally broke the barrier for me wasn't some breakthrough in REST design. It was AI.

Today, I don't waste my life manually mapping JSON payloads into structures. I don’t read API docs line-by-line trying to guess where the edge cases are hiding. I just hand the spec to the machine.

My workflow is now dead simple:
  1. Grab the API key. 
  2. Stash it in the system keychain. 
  3. Give the spec to the AI and let it do the grunt work. 
Pagination, retries, flattening, normalization; the machine handles all the deterministic boring stuff. What I want on the other side isn't an object model.

I want result sets.

Give me the rows and columns. Give me something I can join, constrain, and reason about at rest. 


From OO Friction to Set-Based Speed 

Once that API output is normalized into a set, the friction evaporates. 

I stop worrying about endpoint "shapes" and start thinking about data quality. I stop writing glue code and start defining truth. This is where PL/SQL shines. It doesn't want you to think in objects; it wants you to think in operations over sets. It wants logic close enough to the data that violating a business rule is actually difficult.

APIs that dump object graphs on you fight that model. APIs that deliver result sets fit into it like a glove. 


APIs Are Just Addresses 

If you look at it the right way, APIs aren't applications. They're just addresses.

A table is one physical address. A view is another. A remote API endpoint is just a slightly more annoying address. The "brain"—the business definition—doesn't live at the address. Neither does the "brawn" (the execution).

Those belong in a stable, declarative core. For me, that’s still the database.

With AI translating API specs into tabular, set-friendly forms, external APIs finally behave like first-class data sources instead of OO intrusions. 


The New Era of Data Integration

For a long time, integrating with external APIs felt like a chore because it forced us to abandon our set-based mindset. We were stuck parsing hierarchies instead of querying data. 

That manual struggle is over. 

With AI acting as the translation layer, we can finally overcome that old "impedance mismatch" between objects and sets. We can normalize the world's objects into the sets we need to build real applications. The "API barrier" has turned into a bridge. We can stop worrying about the shape of the payload and get back to what we do best: defining the truth of the data.