WebKit for SwiftUI: Hybrid App Architecture Worth Exploring

TIL about using WebKit views within SwiftUI apps for hybrid mobile/web development. This approach caught my attention as a potential middle ground between native and web apps. I’ve been thinking about architecture patterns for apps that need to work across platforms. The usual suspects are React Native, Flutter, or going full native everywhere. But WebKit in SwiftUI might be an interesting alternative. What’s got me curious is how this compares to other hybrid approaches: ...

June 12, 2025 · 1 min · 184 words

Ecto's `preload` with joins uses a single query!

TIL that when you explicitly join associations in Ecto and use preload, it executes just ONE query: # ✅ Single query - uses the joined data! from(rt in RaceTeam, left_join: r in assoc(rt, :race), left_join: cl in assoc(rt, :clues), where: rt.id == ^team_id, preload: [race: r, clues: cl] ) |> Repo.one() I always thought preload meant multiple queries, but that’s only true without explicit joins: # ❌ Multiple queries RaceTeam |> preload([:race, :clues]) |> Repo.one() The joined version handles the SQL row duplication problem automatically, grouping everything into the proper nested structure. No more N+1 queries AND no manual grouping needed! 🚀 ...

May 28, 2025 · 1 min · 102 words

Adding `dbt-utils` requires `dbt deps

I added dbt-utils to an existing dbt project. Things worked great on dev, but not prod (“worked on my machine!”) The error message was telling me that the dbt_project.yml file was missing from the python dependency for dbt-utils Not the most helpful error message. But I found the fix. I needed to run dbt deps (really uv run dbt deps) to get things fixed After that, no more errors

May 12, 2025 · 1 min · 69 words

Claude + Duckdb + MCP == data engineering productivity boost

I was working on a job for a client and had extracted a subset of production data into a duckdb database. I then set up the DuckDB MCP client (with my read-only mode flag turned on) I then fired up Claude Desktop and entered the following (note some data has been blanked out for privacy): I want to use the PRODUCTION_SCHEDULE table to find gaps in the coming months where the nothing will be running on production lines with `line_number` LINEABC1 and LINEABC2. The table has one row per scheduled run and has START and END columns to show when each run will begin and end. Can you write a query that will show us the gaps in between these runs between today and the end of september, 2025? Claude sonnet 3.7 churned away for about 40 seconds and produced an excellent result: ...

May 1, 2025 · 1 min · 186 words

UUID as Primary Key

While working on DreamersDash I decided to make all primary keys UUIDs instead of auto-incrementing integers. The huge benefit here is that I can generate the id on the client before the record is inserted into the database. Then if I ever need to poll for updates, I already know the ID, so it is very simple to make a GET request for the record with the matching ID.

March 31, 2025 · 1 min · 69 words

FastAPI Dependencies and Profiling

I was building a FastAPI application to serve a PII detector system build using presidio I was loading the model like this: from presidio_analyzer import AnalyzerEngine from presidio_analyzer.nlp_engine import NlpEngineProvider from my_app import get_request_analyzer, OpenAPIURLMatcher, RequestAnalyzer from fastapi import Depends, FastAPI app = FastAPI() def get_presidio_analyzer() -> AnalyzerEngine: nlp_engine = NlpEngineProvider(nlp_configuration=NLP_CONFIG).create_engine() return AnalyzerEngine(nlp_engine=nlp_engine) def get_request_analyzer( analyzer: AnalyzerEngine = Depends(get_presidio_analyzer), ) -> RequestAnalyzer: url_matcher = OpenAPIURLMatcher() return RequestAnalyzer(analyzer=analyzer, url_matcher=url_matcher) @app.post("/my-endpoint-here") def post_data_types( request: DataTypeRequest, analyzer: RequestAnalyzer = Depends(get_request_analyzer), ) -> DataTypeResponse: ... Notice that I was using the FastAPI dependnency injection system ...

January 23, 2025 · 2 min · 255 words

Persistent SSH Port Forwarding

I found myself in a scenario where I needed to be able to run a script on a remote server that would connect to an RDS Postgres instance only exposed to a jump server. The computers involved are: My client machine My remote server The jump server The RDS instance I ended up setting up a persistent ssh tunnel on the remote server using systemd. The unit file is as follows: ...

November 7, 2024 · 1 min · 198 words

Ecto changeset retrieval methods

When working with Ecto.changesets I have a hard time remembering when I should use get_field/3, get_change/3, fetch_field/2 and fetch_change/2 Here’s my summary: the get_* methods are used when want to be able to provide a default value the fetch_* methods return a tuple with the source of the data (or :ok) OR :error the *_field methods will search either the existing data or the changes the *_change methods will search only the changes for the key Short doc snippets for each of the methods ...

November 5, 2024 · 1 min · 165 words

Elixir Update in

The update_in function and Access module of Elixir are extremely powerful. Here’s a snippet I just wrote in Jupyteach: def strip_jupyteach_metadata_from_ipynb_map(ipynb) do ipynb |> update_in(["metadata"], &Map.drop(&1, ["jupyteach"])) |> update_in(["cells", Access.all(), "metadata"], &Map.drop(&1, ["jupyteach"])) end ipynb is a map that follows the Jupyter notebook spec To support some features I jupyteach, I add custom cell metadata to the cells so when we import back in to Jupyteach we know where we are. Some of these metadata entries are sensitive and should be stripped out when exporting for public use or even for student use (we store solutions in the metadata!) ...

October 25, 2024 · 1 min · 161 words