• Jun 22

Claude Said "Here's the Plan." I Should Have Asked These 10 Questions First


Here is what happened the last time I said “looks good, let’s build it.”

I was using Claude Code to build a Databricks ELT pipeline — streaming Airbnb data directly into Unity Catalog Bronze tables in Databricks. Claude had proposed a plan: databricks-sql-connector for the SQL layer, DBFS for file storage, a Personal Access Token for auth. All reasonable. All in the documentation. I reviewed it, approved it, and started implementing.

One hour later — a 15-minute silent hang from a corporate SSL proxy that the connector couldn’t handle. A 403 on file uploads because the PAT was missing the files scope. A catalog naming mismatch that returned SUCCEEDED while loading exactly zero rows. None of the code was wrong. The plan was — and I had approved it without asking the right questions.

If this sounds familiar, you’re not alone. It’s one of the most common traps when working with AI coding tools: Claude gives you the best answer it can within the constraints it knows. If you haven’t told it about your environment — your proxy, your tokens, your permission model, your platform’s ownership rules — it defaults to the cleanest path in the documentation. That path is almost never your path.

The good news: asking better questions before you approve a plan is a learnable skill. These are the 10 I now run through every time.


The Shift

Here is the shift in thinking that changed how I work with Claude Code.

Claude doesn’t know your environment. It knows the documentation — and documentation is almost always written for a clean setup: no corporate proxy, admin access, a fresh workspace with no permission quirks. When you ask Claude to design a plan without briefing it on your specific constraints, it defaults to that clean path. The plan isn’t wrong. It’s just not yours.

The questions below are how you close that gap. Not all of them will apply to every project — but the ones you skip are usually the ones that bite you.


Group 1: Your Environment and Constraints

These are the questions Claude cannot answer without you. If your environment has anything non-standard — a proxy, a VPN, scoped credentials, restricted network access — Claude won’t guess it. You have to tell it. And the cheapest time to do that is before the plan is written, not after the third debugging round.


1. What does this plan assume about my environment that I haven’t told you?Ask this one first, every time. It is a forcing function — it makes Claude surface the assumptions baked into the plan before a single line of code is written.In this project, the original plan assumed an open network, a standard certificate store, and full Databricks access. None of those were true. The corporate network performed SSL inspection using a self-signed CA. The service principal had scoped permissions. One sentence — “I’m behind a corporate proxy with SSL inspection” — would have changed the entire plan before it was approved. Instead, it surfaced 15 minutes into a silent hang, three debugging sessions later.


2. What protocol does this library use under the hood — and does it handle SSL on its own?This question sounds unnecessarily low-level. Ask it anyway.databricks-sql-connector uses the Thrift binary protocol with its own TLS implementation. The requests library uses the macOS system certificate store. These are not the same thing, and they do not behave the same way behind a corporate proxy. The system cert store trusted the corporate CA. Thrift did not. Claude recommended databricks-sql-connector because it appears in the majority of Databricks tutorials — it's the documented default. One question about the underlying transport would have surfaced the risk before the connector was ever installed.


3. Are there scope restrictions on the credentials you’re recommending?

Authentication is not binary. A token can return HTTP 200 on one endpoint and HTTP 403 on another — not because it’s wrong, but because it’s missing a specific scope the API requires.

The Personal Access Token in this project worked perfectly for SQL queries and failed immediately on the Files API with: “Provided access token does not have required scopes: files.” That’s a workspace configuration restriction, not a misconfiguration on my end. Claude recommended a PAT because it’s the standard Databricks authentication recommendation. If I had asked “are there scope restrictions I should verify before we design around this token type?” — the plan would have started with OAuth M2M. Instead, it ended there, after three separate auth failures.

This applies anywhere credentials have scopes: AWS IAM policies, GCP service accounts, GitHub tokens with fine-grained permissions

Group 2: Infrastructure vs. Application

These questions are about a line most plans don’t draw clearly — and Claude won’t draw it for you unless you ask. On one side: the things an admin does once to make the environment ready. On the other: the things your script does on every run. Conflating the two is one of the most common ways a pipeline breaks in a way that is genuinely hard to diagnose, because everything looks correct until you check who owns what.

4. Which steps in this plan are one-time admin setup, and which are recurring script operations?

Most AI-generated plans are self-contained by design. Claude writes code that runs from scratch because that’s what works in a demo environment. In production, that convenience becomes a problem.

In this project, CREATE TABLE IF NOT EXISTS ended up inside the ingestion script because it felt practical — the pipeline could provision its own tables on first run. In Unity Catalog, the principal that creates an object becomes its owner. The service principal ran CREATE TABLE, became the table owner, and the admin user couldn't run SELECT without the SP issuing a GRANT — which the admin couldn't force. A circular dependency, caused entirely by one setup step landing in the wrong place.

The clean split: admin creates tables once in the SQL Editor. Service principal loads data on every run. The script never touches DDL.

Not just a Databricks problem: AWS Glue table creation vs. Glue job execution. Snowflake CREATE SCHEMA vs. pipeline data loads. BigQuery dataset provisioning vs. service account writes. Anywhere there's an ownership or IAM model, this separation matters from day one.


5. Who owns the objects this plan creates — and can everyone who needs access actually get it?This is the follow-up to Question 4, and it’s worth asking separately because the ownership consequences aren’t obvious until you’re locked out.In Unity Catalog, ownership is automatic: whatever principal runs CREATE TABLE owns the table. The admin running GRANT SELECT on a table the service principal created cannot override that — the SP, as owner, is the one who needs to issue the grant. The admin couldn't query their own pipeline's output from the SQL Editor without going back through the service principal. That's not a misconfiguration. That's how the system is designed to work.One question during planning — “If the service principal creates these tables, who becomes the owner? Can the admin user still query them?” — would have surfaced this immediately. Claude’s answer would have been correct and fast. The question was never asked.Not just a Databricks problem: S3 object ownership in cross-account AWS setups. BigQuery datasets where a service account becomes the owner and the human admin can’t modify IAM. Snowflake role hierarchies where a child role creates a table the SYSADMIN can’t touch without an explicit grant chain. Whenever objects are created programmatically, ask who owns them before you build anything around them.


6. What must already exist for this command to work — and does it fail loudly or silently?

Before testing any function, ask what the world needs to look like for it to succeed. Then ask what happens if those conditions aren’t met.

COPY INTO in Databricks does not create a table. It loads data into one that already exists. When the target table was missing, it returned DELTA_MISSING_DELTA_TABLE_COPY_INTO — at least that's a loud failure, easy to diagnose. But the catalog casing issue (PROD_BRONZE vs prod_bronze) returned SUCCEEDED with zero rows loaded. No error. No warning. The pipeline ran, the job completed, and the table was empty.

Silent success is harder to catch than a loud failure. Knowing in advance whether a command can succeed incorrectly — producing no output, no error, and no indication that something went wrong — is the difference between a 30-second pre-condition check and an hour of staring at an empty table.

Not just a Databricks problem: dbt run where a misconfigured ref() resolves to zero rows and the model builds successfully but empty. A Kafka consumer reading from the wrong offset — consuming nothing, reporting no error. An INSERT INTO in Snowflake where a schema mismatch silently skips rows depending on your error handling config. Ask whether the command can succeed while doing nothing — and check for it explicitly.

Group 3: Failure Modes and Verification

By the time you reach implementation, the environment questions are answered and the infrastructure is in place. This group is about what happens at runtime — the failures that don’t announce themselves, the checks that collapse five debugging rounds into one, and the habit of asking Claude to verify its own plan before you build on top of it.


7. After this runs, how do I confirm the output is correct — not just that the job completed?A status of SUCCEEDED is not the same as a result that is correct. This distinction matters more than most engineers expect, because AI-generated plans tend to focus on execution — did the job run — rather than validation — did it do the right thing.COPY INTO returned SUCCEEDED. The table was empty. The job had genuinely succeeded at loading zero rows, because the catalog path was wrong and there were no matching files to load. No error. No warning. Correct behavior on bad inputs.The check that caught it was a simple SELECT COUNT(*) FROM prod_bronze.airbnb.listings WHERE _source_date = '<today>'. One query, run immediately after the job, would have surfaced the issue in seconds. Ask Claude upfront what the positive verification looks like — not the absence of an error, but the presence of the right output.Not just a Databricks problem: a dbt model that builds successfully but materializes zero rows because a WHERE filter is too aggressive. An Airflow task that marks itself success but writes an empty file to S3. A Spark job that completes without errors but silently drops rows on a schema mismatch. Wherever a job can succeed while doing nothing useful, build an output check into your verification step.


8. What naming conventions, case sensitivity, or path formats does this system enforce — and where could a small mismatch cause a silent failure?Ask this one before you write a single constant, environment variable, or file path. Small format mismatches are some of the hardest failures to diagnose because the system doesn’t always tell you that the path it received is simply not the one that exists.Unity Catalog normalizes all names to lowercase in storage paths. The config had DATABRICKS_CATALOG = "PROD_BRONZE". The Volume path became /Volumes/PROD_BRONZE/airbnb/raw_files/ — a path that does not exist. COPY INTO found no files and returned SUCCEEDED. One question — "does Unity Catalog enforce lowercase on catalog and schema names?" — would have set the constant correctly from the start.Not just a Databricks problem: Snowflake unquoted identifiers are silently uppercased, so my_table and MY_TABLE are the same object — until you quote one and not the other. S3 key names are case-sensitive; a path mismatch returns a 404 with no suggestion of what went wrong. Linux file paths vs. Windows paths in a cross-platform pipeline. Ask what the system enforces and standardize it in your config before anything else.


9. Can you give me a verification command for each step in this plan — so I can confirm each piece before building on top of it?This is the question that turns a plan into a plan you can trust. Most AI-generated plans focus on the implementation steps. The verification steps are an afterthought — or missing entirely.Ask Claude to write them as part of the plan. Before testing copy_into_bronze: confirm the URL returns 200. Confirm the Volume path exists. Confirm the table exists and is owned by the right principal. Confirm the catalog name is lowercase. Four checks, each 30 seconds, run in sequence. Every debugging round in Phase 4 of this project mapped to one of those four checks being skipped.One more thing — when Claude gives you a verification command, run it exactly as given. In this project, Claude said to use get_download_urls() to get the live URL before testing the upload function. A hardcoded URL with an older snapshot date was used instead. Inside Airbnb no longer served that file. HTTP 403. An entire debugging round from one deviation.Applies to any stack: test your OAuth token before writing the code that depends on it. Hit your API endpoint with a curl command before building the client. Run your dbt model against one row before running it against the full table. Verification is part of implementation — not a step you do after something breaks.


10. Is this the standard approach from the documentation, or what you’d recommend given the constraints I’ve described?

Save this one for last — it’s the meta-question, and it reframes every answer Claude has given you.

Claude’s default is the documented path. The documented path is written for a clean environment: open network, admin access, no credential restrictions, fresh workspace. That path appears in thousands of tutorials and it works in most of them. It did not work here — not because it was wrong, but because this environment was not the one the documentation imagined.

Asking this question forces Claude to distinguish between the standard answer and the right answer for your setup. The difference between databricks-sql-connector (documented default) and the REST API (right for this environment) was one question. The difference between a PAT (documented default) and OAuth M2M (right for this environment) was one question. The difference between DBFS (documented default) and Unity Catalog Volumes (right for this environment) was one question.

This generalizes completely. The documented approach for any tool is optimized for the person the documentation imagines: a developer on a personal laptop with admin rights and a clean internet connection. If that’s not you — and in most professional environments it isn’t — ask Claude to reconsider the plan given your actual constraints. It will.

One Exchange vs. Fifty

None of this is about slowing down or second-guessing every suggestion Claude makes. The planning step costs almost nothing — Claude is fast, and one focused exchange with the right questions takes maybe ten minutes. What costs time is the alternative: implementing a plan designed for a different environment, debugging failures that were baked into the design from the start, and rebuilding what could have been right on the first try.

These 10 questions replaced roughly 50 debugging exchanges in this project. That’s not an exaggeration — it’s what the token cost analysis showed afterward. The questions that weren’t asked are the ones that showed up later as blockers, each one requiring multiple rounds to diagnose and fix.

You don’t need to ask all 10 every time. Read through the list before you approve any plan. The one you skip is usually the one that comes back.


Quick Reference — 10 Questions to Ask Before You Approve

You can use it as your pre-approval checklist.

Environment & Constraints

  1. What does this plan assume about my environment that I haven’t told you?

  2. What protocol does this library use under the hood — and does it handle SSL on its own?

  3. Are there scope restrictions on the credentials you’re recommending?

Infrastructure vs. Application

  1. Which steps are one-time admin setup, and which are recurring script operations?

  2. Who owns the objects this plan creates — and can everyone who needs access actually get it?

  3. What must already exist for this command to work — and does it fail loudly or silently?

Failure Modes & Verification

  1. After this runs, how do I confirm the output is correct — not just that the job completed?

  2. What naming conventions or case sensitivity rules in this system could cause a silent mismatch?

  3. Can you give me a verification command for each step — so I can confirm each piece before building on top of it?

  4. Is this the standard approach from the documentation, or what you’d recommend given the constraints I’ve described?


The pipeline in this project — streaming Airbnb data from Inside Airbnb into Databricks Bronze tables, incremental and idempotent — works. But the path there consumed roughly twice the tokens it should have. Every wasted exchange maps directly to a question on this list that wasn’t asked upfront.The next step in this project is more interesting: Claude stops being the tool I used to build the pipeline, and starts running inside it — as a data quality agent triggered automatically after every ingestion run. That’s a different article.