Programming
-
Julia: High-Performance Computing Without the Two-Language Tax
I hadn’t heard of Julia until someone posted about it on Bluesky. The language was new to me, so I went reading, and this post is a summary of what stood out. Fair warning: I haven’t written any Julia myself yet.
The thing Julia is built to solve is what people call the two-language problem: you prototype in something pleasant like Python or R, hit a performance wall, then rewrite the slow parts in C or Fortran and glue them back together. Now you maintain two codebases and a wall between the people who know the domain and the people who know the fast code. Julia’s pitch, out of MIT in 2012, is to collapse that into one language: Python-like expressiveness with C-like speed, and no rewrite step.
The Three Things That Make It Different
1. Multiple dispatch
This part reorganizes how you think.
In Python or C++, methods belong to classes. You write
object.method(arg), and the language picks which method to run based on one thing: the type ofobject. That’s single dispatch.Julia flips it. Functions are the top-level concept, and methods are implementations attached to them. When you call a function, Julia picks the exact method based on the concrete types of all the arguments, not just the first one. Same function name, a different method chosen by the full type signature. This replaces the
if isinstance(...)ladders you’d write in Python and lets the type system route the call for you.2. JIT compilation, not interpretation
Julia is dynamically typed, but it does not interpret your code. The first time you call a function with a specific combination of argument types, the JIT compiler (built on LLVM) infers the types across the whole call graph and compiles specialized native machine code. Every call after that, with those same types, runs at C speed.
That’s the trick behind the whole “no rewrite” promise. You didn’t add type annotations everywhere and you didn’t drop into another language. The compiler just specialized for you.
3. Math that looks like math
Julia treats mathematical notation as first-class. Unicode variable names (
α,β,θ), literal coefficients like2x + 3y, and native linear algebra operators. If you’re translating a paper into code, the code ends up looking like the paper:α, β = 3, 4 α^2 + β^2 # => 25 2α + 3β # => 18 (2α means 2 * α, no asterisk needed)The strange thing about programming is how often the math matters. First-class support for mathematical notation sounds minor, but for the tasks Julia is built for, it turns out to be surprisingly useful.
Another Quick Example
One Julia feature that stood out to me is broadcasting. In NumPy, chaining array operations like
sin(x) + cos(y)quietly allocates temporary arrays for each step. In Julia, one dot fuses the whole expression into a single loop with zero intermediate allocations:x = [1.0, 2.0, 3.0, 4.0] y = [10.0, 20.0, 30.0, 40.0] result = [@](https://micro.blog/). sin(x) + sqrt(y) * 2.5 # fused, no temporaries
Tooling and Two Rules
Setup is one installer.
juliaupmanages versions (curl -fsSL https://install.julialang.org | sh, thenjuliaup add release), and the built-in package manager lives one keystroke away: hit]in the REPL to enter package mode, thenadd DataFrames DifferentialEquations Makie CUDA. The ecosystem worth knowing: DifferentialEquations.jl for solvers, JuMP.jl for optimization modeling, Makie.jl for GPU-accelerated plotting, and CUDA.jl for writing GPU kernels without touching CUDA C++.Two rules keep your code at full speed. First, avoid untyped global variables. A non-const global forces a dynamic type lookup on every access, and it will silently gut your performance. Use
const, or pass values in as parameters. Second, keep your functions type-stable, meaning the return type depends on the types of the arguments, not their runtime values. When something feels slow, run@code_warntype my_function(1.0, 2.0)and look for the red. That’s the compiler telling you it couldn’t infer a type.
So When Should You Actually Use It?
Reach for Julia when you’re building numerical simulations, differential equation solvers, actuarial or financial risk models, or custom ML architectures, basically anywhere NumPy or PyTorch hits a wall and you can feel the rewrite coming.
Don’t reach for it to build a web scraper or a CRUD service. Go or Node boots faster and the JIT warmup isn’t worth it there.
The pitch is simple: the language that lets you prototype is the same language that ships. No second codebase, no translation layer, no wall between your domain experts and your fast path. If you’ve ever paid the two-language tax, that’s the whole reason to give Julia a look.
Sources
- Bezanson, Edelman, Karpinski & Shah, Julia: A Fresh Approach to Numerical Computing, SIAM Review 59(1), 2017.
- Julia Documentation, Performance Tips.
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].
-
Adding Types to JSON with Dhall
A few months ago I wrote a post asking whether there’s something better than JSON. Two configuration languages that sit above JSON kept coming up: CUE and Dhall. Both give you the things JSON lacks when you author config by hand, and both compile down to plain JSON, YAML, or whatever your services actually read. I spent more time with CUE back then and never gave Dhall a real look. This post is me going back for that second look, because the one feature I kept wanting was a type system over my config.
JSON is the universal language of API payloads and config files, and I don’t want that to change. But as a format for authoring configuration by hand, it’s rough:
- No comments.
- No variables or functions, so you copy-paste the same block ten times.
- No type system, so
"8080"and8080look equally valid. - No imports, which is how you end up with a 2,000-line monolith nobody wants to touch.
The usual escape hatch is a templating engine like Jinja or Helm, or a real programming language like Python or TypeScript that spits out JSON. That works, but you’ve traded one problem for a scarier one: your config generator is now Turing-complete. It can crash, hang in an infinite loop, or reach out and read some local environment variable, and it’ll do it at 2 AM when the pipeline runs.
This is where Dhall comes in.
What is Dhall?
The short version: Dhall is JSON plus types, plus functions, plus imports. It’s a programmable, strongly-typed configuration language.
The part I actually care about is what it doesn’t have. Dhall is not Turing-complete. No arbitrary recursion, no side effects. Every Dhall program is guaranteed to terminate. You get the abstraction power of a functional language like Haskell or Elm, with the guarantee that it will never hang your build. That’s a different trade than “just write a Python script.”
The problem, in JSON
Here’s a normal
config.jsonfor a microservice:{ "serviceName": "payment-api", "port": 8080, "environment": "production", "database": { "host": "db.internal.net", "maxConnections": 50 } }Three ways can be a problem in production: someone writes
"port": "8080"and the service won’t boot, someone typos"prodution"and it silently runs in debug mode, or someone forgetsmaxConnectionsentirely and you get a null blowup at runtime. Nothing catches any of it until it’s live.The same thing, typed
In Dhall you define the shape up front. Enums, record types, default values:
-- schema.dhall let Environment = < Local | Staging | Production > let Database = { Type = { host : Text, maxConnections : Natural } , default = { maxConnections = 20 } } let Config = { Type = { serviceName : Text , port : Natural , environment : Environment , database : Database.Type } , default = { port = 8080, environment = Environment.Local } } in { Environment, Database, Config }Now you author against that schema, and you get defaults and composition for free:
-- config.dhall let Schema = ./schema.dhall let myConfig : Schema.Config.Type = Schema.Config.default // { serviceName = "payment-api" , environment = Schema.Environment.Production , database = Schema.Database.default // { host = "db.internal.net", maxConnections = 50 } } in myConfigMisspell
Production, or pass"8080"as a string, and Dhall throws a type error before a single line of JSON is generated. Hopefully the benfit is now clear; adding a type safety layer to your config files.Compiling down to JSON
You don’t ship Dhall to your services. You ship the JSON they already understand:
brew install dhall-json dhall-to-json --file config.dhallOut comes clean, boring, standard JSON. Your services never know Dhall was involved. The part that I like is the safety lives at authoring time, and the runtime artifact stays dumb.
Two features worth knowing about
Hermetic imports with hash pinning. Dhall can import from a URL, so shared utilities live in one place instead of being copy-pasted across five repos. To keep someone from swapping the file out from under you, you pin the import to a SHA-256 hash of its normalized form:
let Prelude = https://prelude.dhall-lang.org/v22.0.0/package.dhall sha256:10db4c919c25e4d262db3ed0d1d6120da3e3906673f00e3012c1d14e1963976aIf the remote content changes, the hash won’t match and the build fails. The hash above is just an example, and each Prelude version has its own, so don’t copy it by hand.
dhall freeze --inplace config.dhallcomputes the correct hashes for whatever you’ve imported and pins them automatically.Exhaustive matching with
merge. When you map a union type to output, Dhall makes you handle every variant:let getLogPrefix = \(env : Environment) -> merge { Local = "[DEV] ", Staging = "[STAGE] ", Production = "[PROD] " } envAdd a
QAvariant later, and everymergeblock that touchedEnvironmentfails to compile until you deal with it. No forgottenswitchcase slipping into production. The compiler keeps a running list of everything you now owe it.Is it worth it?
Raw JSON Dhall Type safety None, fails at runtime Static, at compile time Comments & logic No Yes Termination N/A Guaranteed Dependency pinning No SHA-256 Output Consumed directly Compiles to JSON/YAML/TOML For a two-key config file, it doesn’t make sense, but once you’re staring down Kubernetes manifests, a pile of near-identical microservice configs, or anything where a typo takes down a service, the calculus changes. You keep clean static JSON as the thing your services actually read, and you move all the ways-to-get-it-wrong to a place where a compiler catches them first.
Sources
- Dhall Language Tutorial & Cheatsheet: records, union types, default overrides,
dhall-to-json, anddhall freeze. - Dhall language standard on GitHub: the non-Turing-complete design and the semantic integrity hash spec.
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].
-
Graphify: Any input. One graph. Complete recall.
The open-source knowledge graph engine. Turn code, docs, papers, meetings and images into a traversable graph. Build once, grow forever. On-device or cloud.
-
Programming Development links code software engineering language
-
Build Your Own Skills Repo
If you’ve been working with AI coding agents for a while, you’ve probably started collecting workflows. You might not call them that yet, but they’re there. Another name for workflows is Skills.
Some are tiny: run these checks before shipping. Some are project-specific: when you touch this library, preserve this API contract. Some are operational: never print secrets, always summarize the logs. Right now they probably live in chat history, a README note, a shell alias, or your own memory. That works until it doesn’t.
A skills repo is a better shape for this. It’s one place to collect, version, review, and share the workflows that make agents useful in your projects. I built my own yesterday, so let me walk through how I think about it.
A skill is judgment, not a command list
The first mistake is treating a skill like a list of commands. Commands matter, but they’re the easy part. The real value is judgment: when to run the command, what to inspect first, what not to do, how to validate the result, and what risks are specific to this tool.
A good skill makes an agent more careful. It narrows the space of bad decisions. So before you write a single instruction, figure out who the skill is for.
Separate adoption from maintenance
Most serious projects have two audiences: people using the project, and people maintaining it. Those should usually be two different skills.
An adoption skill helps an outside developer get value from your project. Install the package, configure it correctly, use the right import path, migrate existing code, avoid the common mistakes, run the right validation.
A maintainer skill helps contributors work inside the source repo. Understand the layout, run the local quality gate, preserve compatibility promises, follow the release conventions.
Here’s why you need two. If you only write maintainer skills, your repo becomes a private automation folder. If you write adoption skills too, it becomes onboarding infrastructure.
Keep each skill focused
A skill should have a job. Not “everything about this project.” Not a duplicate README. Good skill names are verbs:
integrate,audit,upgrade,migrate,debug,ship. That keeps the trigger obvious, so when someone asks the agent to do that kind of work, the skill has a clear reason to load.If a project needs multiple workflows, split them. A library might have
integrateanddevelop. A deployment system might havedeployandrollback. Don’t cram them into one file.Put the safety rules near the top
The most important part of many skills is the “do not” section:
- Do not print secret values.
- Do not delete or archive anything without confirmation.
- Do not add failing CI enforcement unless asked.
- Do not do broad rewrites before previewing a diff.
- Do not commit local registry URLs.
Agents are good at momentum. Safety rules are how you make that momentum usable. The more destructive the workflow, the more explicit the guardrails should be.
Include validation, not just execution
Every skill should answer one question: how do we know this worked? That might be
pnpm test && pnpm build, orcargo test && cargo clippy -- -D warnings, orgo test ./.... For non-code workflows it might be “export the review list” or “verify the generated config is ignored by git.”This matters because agents can complete every step without completing the work. Validation closes the loop.
Write for the agent inside the repo
Skills should assume the agent is operating in a real project with real files and existing conventions. So the useful instructions look like:
- Inspect
package.jsonbefore choosing a package manager. - Read the existing test scripts before adding new ones.
- Prefer the local task-runner commands when they exist.
- Check the framework boundary before picking an import path.
That’s the context generic model knowledge won’t reliably infer. And it’s why you shouldn’t just copy the README into the skill. A README is for a human browsing the project. A skill is for an agent doing work. They overlap, but they aren’t the same artifact. Keep the skill short enough that loading it is cheap.
Use a marketplace repo as the index
Your skills repo doesn’t need to be the canonical home for every skill. Some projects should own their own plugin metadata, especially if they already have a CLI, release process, and docs. Your marketplace can just point at them remotely. Other skills can live directly in the marketplace repo. One structure that works:
skills/ .claude-plugin/ marketplace.json plugins/ esm/ .claude-plugin/plugin.json skills/develop/SKILL.md upkeep-rs/ .claude-plugin/plugin.json skills/audit/SKILL.mdThe marketplace becomes the thing people add once. Individual plugins stay free to live locally or point at their canonical upstream.
Scan third-party skills before you import them
The moment your marketplace points at someone else’s plugin, you’ve inherited their security posture. And skills are a soft target. The dangerous payload usually isn’t code, it’s prose: an attacker buries instructions inside a
SKILL.md, gated behind an innocent-sounding trigger, that tell the agent to read your.envand send it somewhere. A normal code scanner walks right past that. There’s no malware signature to match. It’s just English.This isn’t hypothetical. Snyk’s ToxicSkills research found prompt injection in 36% of the skills they tested, across more than a thousand malicious payloads. If you’re pulling skills from a public index, some fraction of them are trying to do something you didn’t ask for.
So run a scanner before you add anything you didn’t write. A few worth knowing:
- Snyk agent-scan inventories your installed agents, MCP servers, and skills, then checks them for prompt injection and data-handling problems.
- NVIDIA SkillSpector scans repos, URLs, or single files against a big catalog of patterns: injection, exfiltration, privilege escalation, tool poisoning.
- claude-skill-antivirus is purpose-built for Claude Code skills and runs several detection engines at once.
One caveat worth internalizing: scanning an MCP config can execute it, because starting a stdio server means running the command in the file. Do that in a sandbox, a container or a throwaway VM, not on your main machine. The tool you run to check for danger shouldn’t be the thing that sets it off.
This cuts both ways. If you publish a plugin others will install, a clear “do not” block and an honest description of what the skill touches is part of being a good citizen of the marketplace.
Start with your serious projects
You don’t need a skill for everything. Start where better agent behavior would matter: public libraries people might adopt, CLIs with safety-sensitive workflows, tools with tricky setup, projects with recurring maintenance, systems where mistakes are expensive.
For each one, ask yourself:
- Who is this for: user, maintainer, operator, contributor?
- What’s the concrete task?
- What should the agent inspect first?
- What commands are preferred, and which are dangerous?
- What should never happen silently?
- What validation proves the work succeeded?
Answer those and you have enough to write a useful first skill.
Why it’s worth doing
A skills repo turns scattered project knowledge into reusable operational guidance. But it also forces a better product question. If this project is meant to help people, what would it look like for an AI agent to help them use it well?
That’s a higher bar than “can the agent run the command?” The point isn’t to automate everything. It’s to package the judgment around your tools so the next agent starts from a better place.
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].
-
Web Development Trending Bluesky Feed
Trending web dev topics on bsky.
View the algorithm definition: www.graze.social/feeds/170…
Want to improve it? Post at @hipstsersmoothie.com
-
A Model Context Protocol (MCP) server for the Internet Archive’s Open Library API that enables AI assistants to search for book and author information. - 8enSmith/mcp-open-library
-
How Do You Actually Review Code an Agent Wrote?
We’ve all had the magic moment by now. You fire up an agent in Claude Code, hand it a bunch of tasks, then let it do its thing. It feels like the future.
Right up until you have to hit
git commit.That’s when the magic curdles into a very specific kind of anxiety: how do I actually review this? I didn’t write it. I barely watched it happen. And now I’m supposed to vouch for it.
The AI Reviewer Trap
The obvious move is to fight fire with fire. An AI wrote it, so pipe the diff into an AI PR review tool and let the machines sort it out, right?
I wrote about this back in May: AI code reviewers won’t save you. Having one LLM grade another LLM’s homework can potentially lead to disaster. They share the same blind spots. They’re trained on the same patterns, so they tend to nod along at the same plausible-looking mistakes, and they’re notoriously bad at catching the subtle, systemic logic flaws that span an entire architecture. The bug that matters is rarely on one line. It’s the interaction between four files, and that’s exactly the kind of thing a second LLM waves through.
So you swing the other way and read it yourself, line by line. Every variable assignment, every branch. And that’s exhausting. Worse, it defeats the entire point of using an agent. If I have to mentally re-type every line the agent produced, I might as well have typed it for real.
So we’re stuck between a reviewer we can’t trust and a review process that erases the speedup. Neither one is the answer.
The Bottleneck Just Moved
Agentic development didn’t make software engineering easier. It moved the hard part.
Writing the implementation used to be the bottleneck. That’s the part the agent is genuinely good at now. What it can’t do for you is tell you the behavior is correct. Verifying the behavior is the whole job now, and that’s a different skill than writing code.
This is why migrating my test suite to Vitest earlier this year has paid off more than I expected at the time. When you’re driving autonomous agents, automated testing stops being a chore you do to keep a coverage badge green. It becomes the only safety net you actually have.
Trust the Spec, Not the Code
In an agentic workflow, my job as the human isn’t to write the function anymore. It’s to write the tests, or at least to rigorously verify the ones the agent proposes.
Think about what that buys you. If I have a comprehensive, fast test suite, I don’t ALWAYS need to read all 400 lines Claude Code just generated. I need to watch the runner light up green. If the tests pass, and the tests are good, the implementation details matter a lot less than they used to. The tests are the contract. The code is just one way to satisfy it.
That second condition is doing a lot of work, though, so I want to be honest about it. “If the tests are good” is the entire game. A passing suite that doesn’t cover the edge cases is worse than no suite, because it hands you false confidence at the exact moment you’ve stopped reading the code. So the scrutiny doesn’t disappear. It relocates. Instead of reviewing the implementation, you review the spec. Are the right behaviors tested? Are the failure modes tested? Did the agent quietly write a test that asserts its own bug?
That’s a much smaller surface to review than 400 lines of implementation, and it’s a far more durable thing to spend your attention on. The tests outlive any single refactor.
TDD Didn’t Die, It Got Promoted
For years people treated test-driven development as a discipline you adopted if you were virtuous and skipped if you were busy. Agentic coding flipped that. It made testing the load-bearing skill, because tests are now the interface between what you want and what the machine builds.
So the answer to “how do I review code an agent wrote” turns out to be: mostly, you don’t. You review what it’s supposed to do, you encode that in tests you trust, and you let the green checkmark tell you whether the agent got there.
I’m curious whether this matches your experience. I’ve found I write more tests now than I did before the agents showed up, not fewer. The implementation got cheap. Being sure it works did not.
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].
-
The Agentic Dev Space Is Moving Fast, and I'm Having a Blast
Step back and look at the last six months of software development. It’s almost hard to believe how much has shifted. Back in January we were doing “vibe coding” with basic autocomplete. By June we’re handing fully autonomous agents a feature, walking off to grab a coffee, and coming back to find it architected, written, and tested.
It’s easy to look at the volume of new tools dropping every single day and feel a little fatigued. But honestly? I’m just excited. This is one of the most fun eras of programming I’ve worked through in decades.
Here’s a quick look at the tools that have already changed my workflow, and the ones I want to dig into next.
The Tools I’ve Adopted
If you read my post earlier this week, you know Supacode has become my daily driver. It bridged the gap between raw terminal access and a UI that understands how context-heavy agentic work is. That’s a harder problem than it sounds, and it’s really feeling good so far.
I also have to shout out the tools that paved the way earlier this year. Claude Code and Antigravity proved out the model of a CLI-native agent that could navigate your file system and do the work. Running Claude Code daily—and turning to Antigravity occasionally when I have access—has completely retrained my habits. I stopped typing every line of code and started acting more like a technical lead reviewing pull requests from a tireless junior developer. That shift in posture is the real unlock, and these were the tools that taught me it.
The Tools on My Radar
Because the space moves so fast, my “stuff I want to learn” backlog keeps growing faster than I can clear it. Here are the three I’m most eager to explore when I carve out the time:
- Hermes. I keep hearing great things about how it handles multi-step reasoning and huge context windows. I haven’t had the right project to throw at it yet, but it’s at the top of the list.
- Pi Agents. The concept here is highly specialized, networked agents working in tandem. Instead of one monolithic agent doing everything, you’d have a “frontend agent” talking to a “database agent.” That feels like a different way to structure the work, and I want to see if it holds up in practice.
- Evaluating Memory Systems. I’ve been diving into how agents remember things over time. I wrote a Mem0 MCP server that runs locally, and I’ve added hooks for Mem0 right into my CLI agents. It’s been a great way to slowly improve my own local agentic memory system. I’m really eager to see how other memory systems (like LangChain’s implementations) work under the hood, and I want to spend more time comparing and contrasting them.
You Don’t Have to Learn It All Today
The best part about this explosion of tooling is that you don’t need to know all of it. You don’t.
Find a tool that solves an immediate problem in your workflow, master that one, and let the rest sit on your radar until you need them. The ecosystem will keep evolving whether or not you’re watching every release. The tools will keep getting better. Chasing every launch is a great way to learn nothing well.
So pick one. Get good at it. Let the backlog wait.
What’s the one agentic tool sitting on your radar that you just haven’t had time to dig into yet?
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].
-
Why Supacode Became My Daily Driver for Claude Code
The number of ways to get an AI to write your code right now is … great? Claude Code, Codex, Opencode, and all the others. I think that’s a good thing. The easier we make it to hand real work to a capable coding agent, the better off we all are in the long run. I want to try most of them because that’s half the fun, figuring things out.
My cool tool for the week is Supacode.
What Supacode Is
Supacode is a native macOS app, written in Swift and built on libghostty, with no Electron and no web wrapper anywhere in sight. It’s fast the way native software is fast, the kind of speed you feel in every keystroke.
The website calls it a “command center for coding agents,” and well ok I get it now. Supacode is the nicest way I’ve found to run Claude Code. It’s open source too, so you can poke at the internals on GitHub if you’re curious.
What It Does for Me
This is the part that I like.
Supacode keeps all my projects organized down the left side, so jumping between them is one click instead of a mental map of which terminal tab is which. When I start a Claude Code session, its chat window moves up into the active area, and I can see every live session I have going at a glance.
The big one: it hooks into Claude Code, so Claude tells Supacode when it’s finished a task or when it needs something from me. Instead of babysitting a terminal waiting for the next prompt, I just get told when Claude is waiting on me. I tried for a long time to get something like that working in a plain terminal and never really nailed it. Here it just works.
And when I need more than one CLI open on the same project, I can group those tabs together instead of squinting at a wall of identical-looking shells.
None of that is glamorous. It’s just the difference between running Claude Code and pleasantly running Claude Code.
I Still Love Ghostty
I’m not leaving Ghostty behind, to be clear. I love Ghostty. Mitchell Hashimoto and everyone who’s worked on it has built something special. When I need a clean shell to compile a binary or grep through some logs, Ghostty is where I want to live.
Supacode is built on libghostty, the same engine that powers Ghostty. So the speed and feel I love about Ghostty is sitting right underneath Supacode too. They’re cousins. I get Ghostty for raw terminal work and Supacode for running Claude Code, and both of them are quietly standing on the same excellent foundation.
So Here We Are
We’re spoiled for choice with AI coding tools right now, and I love that. Most of what I try is interesting, and then I move on. Every so often something just slots into how I already work and earns a permanent spot. For me, right now, that’s Supacode. If you’re running Claude Code all day on a Mac, it’s well worth a look.
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].