Author: Richard Seroter

  • Daily Reading List – August 10, 2026 (#842)

    Great weekend, and great Monday. Today’s reading list has some bangers on it with some hard truths to digest.

    [blog] Introducing Muse Glimmer: An Open Agentic Model That Runs on Your Device. Some great model drops by Meta lately. This one is small and agent-ready. More here.

    [blog] “Code was never the hard part” is an insult to all programmers. Agree. Stop saying this. Coding isn’t easy. If “figuring out what to build” is the hard stuff, how come PMs aren’t the tech superstars? Some honest perspective here.

    [blog] PDFs are terrible. Amusing anecdote where Accenture realizes that non-engineers are eating a lot of tokens doing things like converting PDFs to easier file formats.

    [article] Managing 8 engineers now feels like managing 4 teams. It’s a new challenge for managers, but the point here is that product management just became the new bottleneck.

    [blog] Decoupling in Software Architecture Moves Complexity. Right. You’re not picking up simplicity as you distribute more components of your system. Other benefits, sure, but the complexity remains.

    [article] What are code reviews even for? Yes, dramatically increasing the volume of code while retaining the same code review process is unsustainable. Something has to change. Just make sure you don’t lose an important means of knowledge transfer or building ownership.

    [blog] Agents Don’t Magically Understand Your API. You still need good API design, even if AI is the API consumer.

    [article] Why observability doesn’t explain what happened. The piece argues that observability tools tell you what’s going on in your system right now. But “why is this happening” and “what triggered it” are likely sitting outside its reach.

    [article] Elevating Antigravity agent skills, Part 4: Subagent messaging. This type of session messaging is new for Claude, but it’s been part of Antigravity for a while. James has a good piece with advice and anti-patterns.

    [article] Platform Engineering ROI: What it costs to build your own platform. It’s usually more than you think. Sometimes less, but it’s easy to leave out hidden costs that pile up or distract your team with undifferentiated work.

    [article] Why Open Source Matters for AI. It’s an “and.” I don’t mind closed models, and many users don’t either. But open weight models make a whole additional set of use cases possible.

    [blog] Open-source is NOT the same as open-weight. Virtually no one gives you an open source model. You can’t change the raw data or pre-processing. All you get is frozen weights to manipulate.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – August 7, 2026 (#841)

    My “no-meeting Friday” was in shambles today with eight meetings. Fortunately those were productive meetings. Now I’m off to see my favorite band perform, and enjoy a sunny weekend. Could be worse!

    [article] Knowing When to Stop: The Art of Making a Loop Converge. “Definition of done” is an important standard to set. For agents too. Be good about setting verifiers at each cycle to avoid endless refinement.

    [blog] Guest Posts: Fake Clocks, Real Guarantees: Inside Go’s synctest. This offers an absurd level of detail, but I love when people invest in sharing this depth of knowledge.

    [article] GitHub pushes stacked pull requests into public preview. I saw people chirping about this online (“is it necessary or not”) but being able to break up large code changes into a smaller set of PRs doesn’t seem like a bad thing.

    [blog] Give any website a WebMCP interface. Great to see this! WebMCP is still early, but I like giving agents a way to efficiently “understand” your web app.

    [blog] Flutter Q2 2026 survey — trust, transparency, and an evolving community. This is a vibrant and happy community. But it’s important that these community surveys also bubble up areas of improvement, which this one did.

    [blog] Rewrite All the Code, All the Time. Will code end up being the replaceable byproduct of specifications? Maybe. This argues we still need to refine the semantics we use to describe our systems.

    [blog] Go 1.27 interactive tour. See what’s new in this upcoming release, and actually execute the commands from within the post.

    [article] InfoQ Culture and Methods Trends Report – 2026. A lot is changing, right? This post helps you visualize what’s super nascent, and what practices are in the late majority.

    [blog] How and Why Netflix Built a Real-Time Distributed Graph: Part 3 — Querying the graph with gRPC execution API. The Netflix engineering team goes deep into their distributed graph that has some amazing response times.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – August 6, 2026 (#840)

    My boss gave me some advice today that I didn’t want, but it’s what I needed. Operational work (budgets, coordination, etc) saps my soul, but there’s a “right way” to present info to higher ups. Lesson learned!

    [blog] Enable on-demand expertise with Agent Skills in Genkit Go. I built my first thing with Genkit Go yesterday. It’s the best AI SDK for Go and has some cool features like this.

    [article] Today’s Codex will feel “primitive” by fall — and its own team’s roadmap backs it up. What’s the next leap forward for these AI builder tools? I don’t know. But it’s coming fast.

    [blog] Agent Plugins package your skills, tools, and more. I’m excited about this. Now we can better group together the skills and MCPs that address a given solution set.

    [article] Should You Self-Host Inference? Important question, and here’s some input for your heuristic. This seems pretty reasonable to me.

    [blog] A new way to website. Static sites aren’t new, but we can throw away many old assumptions about how to create and run our ideas.

    [blog] Scaling AI Agent Infrastructure with the MCP Stateless updates. Excellent post from those who led the charge to decouple the protocol from the transport. Big implications!

    [blog] Introducing Muse Code and Muse Spark 1.2. Everybody gets a coding CLI! They’ve got a strong feature set and early benchmark numbers.

    [article] What nobody tells you about writing agent skills. Great advice. Read this to gather some battle-tested knowledge about what to include in your skills, how to maintain them, and whether you even need one.

    [blog] Agent and Model Evaluations in Gemini Enterprise Agent Platform are now GA. This looks great. I started to try it out yesterday, but ran out of time and couldn’t do it justice. I’ll put together a blog post at some point.

    [blog] Simon Willison on Technical Blogging. Great advice. Just write. Most people will never know what you skipped to get it out the door.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – August 5, 2026 (#839)

    I procrastinated on something I should have written up today at work, but I’m using AI to first play with the idea a bit more. And while that AI was running, I was able to start experimenting with a few new products I’d been meaning to give a whirl. Are you still figuring out how to use that “AI processing time” effectively? I am.

    [blog] The next chapter of our AI momentum. Some pretty big news in the Google universe. Google DeepMind is in good hands. Related.

    [article] The Shape of Things to Come. Long piece, but Steve’s a forward thinker and it’s useful to see what he’s pondering regarding AI workflows. The CI/CD discussion was particularly enlightening.

    [blog] A unified API for AI model routing. I think I like this, but need to go hands on and understand it better. Given today’s token cost considerations, many people are looking for model routing options.

    [article] We Keep Renaming AI Coding. Here’s What I’d Call It. Maybe “AI-driven development” is the best of the lot. You can find a flaw in any of them.

    [blog] The Agent Development Lifecycle has arrived on Cloudflare. I like seeing how everyone arranges this new dev lifecycle. Cloudflare does innovative work, so I pay attention to how they think about it. Cloudflare OS also looks cool.

    [blog] The AI productivity gap. Somewhat related to the previous item, make sure you understand the complexity of the developer job.

    [article] Barbell-Shaped Product Roles. Coding and shipping faster doesn’t make product thinking less important. It’s the opposite.

    [blog] Unlocking the future of shared storage: Filestore on Colossus. When you use Google Cloud, you get to take advantage of the best infrastructure on the planet.

    [article] What if you’re not supposed to have a long-term plan? Are you doing what you thought you’d be doing? Did five-year-old you have your career mapped out perfectly? If so, kudos. But the rest of us have had a more emergent path.

    [article] What to Consider Before Giving Advice on a Global Team. I don’t think enough about this, and need to. One style doesn’t fit all.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – August 4, 2026 (#838)

    Today’s list had a bunch of fresh insights to learn from. I liked the perspective on model retention terms, working with agent teams, where the AI moat lives, and who builders are.

    [article] Elevating Antigravity Agent skills with image generation. For now, skills represent the best way to steer the harness and avoid wasting tokens on unnecessary turns. Good example here.

    [blog] Governed Growth, Part 2: The Retention Window That Quietly Shrank. Casey continues his exploration of an area you probably haven’t dug into much. Maybe you don’t read retention terms, but there’s some important stuff in there.

    [blog] How I Work With 5 Coding Agents Simultaneously. I’d imagine that coffee is involved. This seems like reasonable advice for those orchestrating fleets.

    [blog] Scaling real-time AI agents with session-aware load balancing. I haven’t seen this talked about much either. It’s not just about load balancing the requests, but sessions themselves.

    [article] The Next AI Moat Isn’t a Better Model. This looks at physical AI (machines) and building learning systems. Really smart point about the “cost” of incorporating new models, and why the broader system is where value accrues.

    [article] Engineering management is a career change, not a promotion. So much great advice here. We under-train managers, and many people don’t realize that it’s an entirely different job.

    [blog] Introducing Database Operations Agents: The future of autonomous database management. These sorts of agents give everyone the opportunity to do a decent job, and database experts an extra superpower.

    [blog] Microsoft Q4 Cloud Growth Rate Slips to 27%, RPO Soars 84% to $678 Billion. All the hyperscale clouds are doing great. AWS just nailed it too. Acceleration rates are different.

    [blog] Securing a Go Supply Chain: The Pipeline That Holds in 2026. Are you playing offense or defense on your supply chain checklist? This approach feels like playing offense where you’re attacking the problem.

    [blog] Who is a Builder? Anyone can be a builder, using this definition. And we’re seeing all sorts of people pick up this new class of tool to solve a problem with software.

    [blog] Software abundance. Spot on. If you’re broadening your skills and embracing a growth mindset, you’re golden right now.

    [blog] GenRec: Towards LLM-Native Recommendation at Netflix. They’ve got some statistically meaningful improvement from an LLM-backed ranker, as opposed to a traditional recommendation model.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – August 3, 2026 (#837)

    I’m back today with a big reading list. The short weekend in Seattle was awesome, and I got to see some people I’ve missed since moving away four years ago. I also missed Taco Time, and had to stop there. No apologies.

    [blog] Batten Down Your Packages: Mitigation Guidance for Supply Chain Compromise. Learn more about supply chain compromises, and how to protect yourself against this ever-present risk.

    [blog] Modern Software Registries are a Trust Service. Related to above. Identity and provenance play a huge part in establishing (and maintaining) trust.

    [blog] What happens to a lawyer’s business model when AI makes him 5x faster. There’s a giant set of new “builders” out there. How much will they build? Will they build regularly? Do they have loyalties to a specific stack? I’m not sure anyone knows, but don’t ignore this constituency.

    [article] The Economic Benefit of Refactoring. If you have less code, you spend less money on tokens when your AI tool reads it. This is an example of refactoring some bloated AI-generated code and seeing the benefits.

    [blog] the one line 95% of agent skills are missing. We’re skipping the instruction that tells the AI tool when an agent should load them.

    [blog] From ‘Write a Python Script’ to Multi-Agent Mastery: My Journey to Becoming an AI Builder. We can build in areas outside our core knowledge areas. Amanda might not have been an audio/visual app developer, but that didn’t stop her.

    [article] Research: How AI Agents Broaden the Scope of Knowledge Work. It’s not just about doing the current work better. It’s about different work.

    [blog] Behind the scenes: How we build, test, and scale Google Agent Skills. How do you create skills at scale? I’m very proud of my team’s work here, and the engineering discipline they put into the effort.

    [blog] How Agentic Coding Is Reshaping the Software Development Lifecycle. Very interesting lens on the new SDLC. Are you building for the always-on PM? Software factories that store, build, and secure code differently?

    [blog] Do more with less: How GKE can reduce your cost per agent by 75%. From 61 OpenClaw agents on a vanilla Kubernetes node to 274 with a cost optimized, high-density configuration. Pretty good!

    [blog] Giving and taking credit in big tech companies. Do you feel a little weird loudly taking credit for something? Probably, unless you’re a narcissistic psycho. But remember that you need to both take and give credit generously in a corporate environment.

    [blog] The borderless Lakehouse: Bring AWS, Databricks and Snowflake data to your AI agents. Looks legit. Understand and query data regardless of its physical location.

    [blog] Stronger with every update: How we’re making Chrome and the web safer in the AI Era. Excellent post that offers lessons for anyone trying to improve product quality.

    [blog] Who’s Writing Open Source Code? Very interesting analysis. Are AI robots writing all the committed open source code now? Not remotely the case (yet).

    [blog] Adapting open source practices to an AI-first world: A retrospective on 2025. Related to the previous one, thousands of Googlers are actively contributing to open source projects.

    [blog] TypeScript Just Got 10x Faster by Not Being TypeScript. Go was a smart choice by this team. Performance is great, and Go suited the compiler’s needs.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – July 30, 2026 (#836)

    I’m on vacation tomorrow, so probably no reading list. Which isn’t good, since I still have a queue of dozens of items to filter though. Maybe you’ll get a bonus weekend edition.

    [blog] Gemini Robotics 2 brings whole body intelligence to robots. Robotics is about to take the leap. This is a remarkable update that opens up all sorts of possibilities.

    [article] What the Hell Is a Loop, Anyway? It’s not too late to ask. This author identifies four different “loops” that we’re talking about now. Maybe five?

    [blog] 7 Times Faster and Cheaper? Gemini 3.5 Flash Lite and Gemini 3.6 Flash in Google Cloud Databases. The time-to-first-token response is remarkable with these latest Gemini models. Gleb takes them for a spin in our databases.

    [article] Demand outweighs supply for AI-skilled workers. Develop skills that others will pay for. And now’s a good time to have certain skills.

    [article] Measuring the impact of AI coding tools: Capacity, not horsepower. “Innovation capacity” is an interesting metric category. Better than trying to land on a “developer horsepower” number?

    [blog] Eval-driven development: Lessons from evaluating GenAI at scale. Something like this might become a way we build out high-quality AI systems.

    [blog] Eval-Driven Design Systems (Part 1). This team made their design system “legible to AI” to see if AI could use it correctly.

    [blog] Transform any place with Nano Banana in Google Earth. What in the world is this. Literally. It’s amazing to create custom images out of real locations. I just tried it myself.

    [blog] CosmosEscape: Taking Over Every Database in Azure Cosmos DB. Yowza. This was a very serious security issue that has since been remediated.

    [blog] GitHub is the wrong shape for this new world. Are we trying to fit solutions for humans onto AI-first workflows? Maybe, and this post calls out where that falls apart.

    [article] The Orchestrator’s Tax. I enjoy reading things like because we’re all figuring out the new dynamics of how we work effectively.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Which programming languages offer built-in tools for modernizing your code?

    If you’ve been a software developer for more than five minutes, then you probably have some old code running somewhere. It probably works fine, so who cares if it’s a little dusty and based on an n-5 language version? Remember that upgrades are an important means for grabbing security patches and performance improvements. But those syntax and architectural changes are important too.

    I wondered which popular languages provided deterministic tools for upgrading code to the latest version. Let’s see what Go, Java, C#, JavaScript, Python, and Rust have to offer.

    Go offers the built-in go fix tool

    Bias alert: I lead the product and engineering of Go at Google. But I’ve also used Go for years before that org shift happened a few months back.

    Go includes syntax modernization directly inside the standard toolchain. It uses modular static analyzers to inspect packages and automatically rewrite legacy code patterns. Go projects are simple—no random XML or JSON settings, no “project” files—so this can be a focused, efficient tool.

    How do you use it? Update your Go toolchain version in the go.mod file. Run go fix. That’s it. This tool was recently rewritten atop the go/analysis engine to be even more powerful.

    Let’s see an example. Maybe I’ve got a CLI tool written in idiomatic Go 1.18 code. This CLI tool renames image files. The code does safe concurrency and uses a helper for slices.

    package main
    
    import (
    	"flag"
    	"fmt"
    	"io/fs"
    	"log"
    	"os"
    	"path/filepath"
    	"strings"
    	"sync"
    )
    
    // Supported image extensions (Go 1.18 slice)
    var imageExts = []string{".jpg", ".jpeg", ".png", ".gif", ".webp"}
    
    // RenameRegistry ensures destination filenames are unique, resolving collisions thread-safely.
    type RenameRegistry struct {
    	mu    sync.Mutex
    	names map[string]int
    }
    
    func NewRenameRegistry() *RenameRegistry {
    	return &RenameRegistry{
    		names: make(map[string]int),
    	}
    }
    
    // GetUniqueName returns a collision-free filename in the destination directory.
    func (r *RenameRegistry) GetUniqueName(base, ext, destDir string) string {
    	r.mu.Lock()
    	defer r.mu.Unlock()
    
    	count := r.names[base]
    	r.names[base] = count + 1
    
    	var targetName string
    	if count == 0 {
    		targetName = base + ext
    	} else {
    		targetName = fmt.Sprintf("%s_%d%s", base, count, ext)
    	}
    
    	// Double check disk existence to avoid overwriting existing files
    	for {
    		targetPath := filepath.Join(destDir, targetName)
    		if _, err := os.Stat(targetPath); os.IsNotExist(err) {
    			break
    		}
    		// If it exists, increment the counter and try again
    		count++
    		r.names[base] = count + 1
    		targetName = fmt.Sprintf("%s_%d%s", base, count, ext)
    	}
    
    	return targetName
    }
    
    // isImageFile checks if a file has a supported image extension (pre-slices inline check)
    func isImageFile(path string) bool {
    	ext := strings.ToLower(filepath.Ext(path))
    	for _, item := range imageExts {
    		if item == ext {
    			return true
    		}
    	}
    	return false
    }
    
    func main() {
    	// Parse CLI flags
    	srcDir := flag.String("src", ".", "Source directory containing photos")
    	destDir := flag.String("dest", "", "Destination directory for renamed photos (defaults to source)")
    	dryRun := flag.Bool("dry", false, "Dry run mode (lists proposed changes without executing)")
    	verbose := flag.Bool("verbose", false, "Enable verbose logging output")
    	flag.Parse()
    
    	// Default destination to source directory if not specified
    	if *destDir == "" {
    		*destDir = *srcDir
    	}
    
    	// Clean paths
    	*srcDir = filepath.Clean(*srcDir)
    	*destDir = filepath.Clean(*destDir)
    
    	log.Printf("Starting photorename CLI...")
    	log.Printf("Source directory: %s", *srcDir)
    	log.Printf("Destination directory: %s", *destDir)
    	if *dryRun {
    		log.Printf("DRY RUN MODE ENABLED - No files will be moved or renamed.")
    	}
    
    	// Scan source directory recursively
    	var photos []string
    	err := filepath.WalkDir(*srcDir, func(path string, d fs.DirEntry, err error) error {
    		if err != nil {
    			return err
    		}
    		if !d.IsDir() && isImageFile(path) {
    			photos = append(photos, path)
    		}
    		return nil
    	})
    
    	if err != nil {
    		log.Fatalf("Error scanning source directory: %v", err)
    	}
    
    	totalPhotos := len(photos)
    	log.Printf("Found %d photo(s) to process.", totalPhotos)
    	if totalPhotos == 0 {
    		return
    	}
    
    	// Ensure destination directory exists (unless dry run)
    	if !*dryRun {
    		if err := os.MkdirAll(*destDir, 0755); err != nil {
    			log.Fatalf("Failed to create destination directory: %v", err)
    		}
    	}
    
    	registry := NewRenameRegistry()
    	var wg sync.WaitGroup
    	// Allocate worker pool limit using custom new helper (pre-Go 1.26 newexpr target)
    	limit := newInt(4)
    	// Limit concurrency using a semaphore (buffered channel)
    	sem := make(chan struct{}, *limit)
    
    	var successCount int
    	var successMu sync.Mutex
    
    	for _, photoPath := range photos {
    		photoPath := photoPath
    		wg.Add(1)
    		sem <- struct{}{}
    
    		go func() {
    			defer wg.Done()
    			defer func() { <-sem }()
    
    			logDebug(*verbose, "[Processing] %s", photoPath)
    
    			// Get file info for modification time
    			info, err := os.Stat(photoPath)
    			if err != nil {
    				log.Printf("Error stating file %s: %v", photoPath, err)
    				return
    			}
    
    			// Format modification time as YYYYMMDD_HHMMSS
    			modTime := info.ModTime()
    			baseName := fmt.Sprintf("IMG_%s", modTime.Format("20060102_150405"))
    			ext := strings.ToLower(filepath.Ext(photoPath))
    
    			// Get unique target name to avoid collisions
    			targetName := registry.GetUniqueName(baseName, ext, *destDir)
    			targetPath := filepath.Join(*destDir, targetName)
    
    			if *dryRun {
    				log.Printf("[Dry-Run] Would rename: %s -> %s", photoPath, targetPath)
    				successMu.Lock()
    				successCount++
    				successMu.Unlock()
    				return
    			}
    
    			// Perform the rename/move operation
    			err = os.Rename(photoPath, targetPath)
    			if err != nil {
    				log.Printf("Rename failed for %s -> %s, attempting copy: %v", photoPath, targetPath, err)
    			} else {
    				logDebug(*verbose, "[Success] Renamed: %s -> %s", photoPath, targetPath)
    				successMu.Lock()
    				successCount++
    				successMu.Unlock()
    			}
    		}()
    	}
    
    	wg.Wait()
    
    	if *dryRun {
    		log.Printf("Dry run complete. Checked %d/%d photos.", successCount, totalPhotos)
    	} else {
    		log.Printf("Renaming complete. Successfully processed %d/%d photos.", successCount, totalPhotos)
    	}
    }
    
    // logDebug helper that prints debug logs using interface{} (pre-Go 1.18 any)
    func logDebug(verbose bool, format string, args ...interface{}) {
    	if verbose {
    		log.Printf(format, args...)
    	}
    }
    
    // newInt is a custom helper to return a pointer to an int value (pre-Go 1.26 newexpr helper target)
    func newInt(x int) *int {
    	return &x
    }
    
    

    On disk, my app contained a folder of randomly named pictures in various folders, as well as a misplaced text file that should be ignored:

    After building and running the tool, it takes milliseconds to complete. And I end up with renamed files in a fresh folder.

    Let’s modernize! I updated my go.mod to now reference Go 1.26.

    go mod edit -go=1.26
    

    Then ran the following command to do a dry run and see what it would change.

    go fix -diff ./...
    

    I get back a +/- view in the console that shows me which modernizers kicked in.

    It won’t find EVERY opportunity to upgrade the code, as it looks for specific patterns. But in this case, it refactored legacy handling of slices, deleted some now-unnecessary code for loop variable shadowing, upgraded an interface parameter to any, and improved a pointer. All in under a second.

    Go has a backwards compatibility promise, so it doesn’t need an aggressive modernizer. Instead, a first-party tool like go fix —which is an Abstract Syntax Tree (AST) that focuses on grammar and structural shape of source code—is meant for continuous codebase modernizations.

    And I’ll note that Dart, the language that powers the popular Flutter framework, also has a pretty great dart fix command that does similar things. Google is pretty good at these things.

    Java has built-in analysis tools, but relies on third party modernization tools

    Modernizing Java code is a lot more than updating your build file! Bumping the version in your maven/gradle file simply tells the compiler what byte code to emit, but your source code doesn’t change.

    The JDK does ship with diagnostic tools to help you identify upgrade issues, but these are read-only. No proactive rewrites. Useful JDK tools include:

    • jdeps: This scans your class files and JARs to report any static dependencies. It helps you get ready for migrations by finding deprecated features, broken third-party tools, and any references to restricted internal JDK APIs.
    • jdeprscan: Another static analysis tool that scans class files, directories, and JAR files for any use of deprecated Java APIs.

    Many Java rewrites start in the IDE, like IntelliJ. There are some built-in analyzers and code fixing tools. You’re constrained to the GUI here, so people doing at-scale rewrites often use OpenRewrite. This build plug-in can upgrade syntax and do framework refactoring.

    Given its established enterprise presence, Java has many documented modernization practices and a strong ecosystem of vendor-created modernization tools. But don’t look deeply into the built-in toolchain for a lot of support.

    C# (.NET) provides a built in upgrade assistant

    For some reason, .NET has had an (unnecessarily?) exciting journey. That means you need some industrial-strength modernization tooling to get you from one major iteration to the next.

    Microsoft doesn’t bake any modernization tools directly into the language toolchain. They introduced the .NET Upgrade Assistant years ago, powered by their Roslyn compiler platform. You could run this sophisticated tool as a Visual Studio plug-in or via a standalone CLI. It scanned your code and then helped you upgrade to the latest version. It covered a lot of ground, because .NET projects are relatively heavy and the framework has seen some major architectural changes over the years.

    However, this tool is now deprecated in favor of the GitHub Copilot modernization chat agent. That’s a very different solution. Now you have commercial implications that impact who can access it. It’s online-only, and internet-dependent. You’re not running this easily in a headless fashion. And now it’s non-deterministic, and not following preset rules. That’s also a “plus” as you’ll probably see deeper modernizations, self-correcting loops, and more flexibility. But some big token costs!

    Like Java, C#/.NET has a massive community and plenty of well-documented practices. The ever-changing tools landscape means those practices aren’t timeless, but it’s not hard to find experts to help you modernize .NET apps. Just don’t look at the framework for a major assist.

    JavaScript doesn’t offer built-in tools for modernization

    You won’t find a built-in modernizer for JavaScript/TypeScript. It’s a fluid, fast-moving, and fragmented ecosystem with different pace layers. You’ve got the language-level updates (ECMAScript) and then the framework-level (React, Next.js, Angular, Vue). Instead of using heavyweight Node.js-native AST-style parsers, developers are embracing faster structural engines and framework CLI orchestrators.

    These developers relied heavily on codemods, like jscodeshift. But that can be heavy and slow on giant repos. There’s a new class of Rust-based codemod engines like ast-grep, jssg, and GritQL.

    You’ve also got framework-first codemod orchestrators like the upgrade CLI baked into Next.js . Angular provides an ng update experience. Tailwind also offers a version upgrade tool.

    JavaScript is probably too decentralized to offer a single, language-included code modernization tool. But the ecosystem has stepped up with useful options.

    Python relies on third party modernizers

    Plenty of devs are still scarred by the Python 2 to Python 3 migration. Python 3 came out in 2008, but Python 2 wasn’t retired until 2020. This wasn’t a simple version bump, but what felt like a whole different programming language. Python 3 did ship with an official AST-based modernization tool called 2to3. But it had issues, and didn’t make migrations easy.

    Today’s Python doesn’t include any built-in source code rewriting tools. For any minor release upgrade, modernizing this code is entirely an ecosystem (third party) story. The built-in tooling basically covers diagnostics and signaling of deprecation. You get runtime warnings for deprecated API use, and compiler warnings for risky or outdated control flows. That’s about it.

    Here’s where the ecosystem stepped up. You have syntax fixers like ruff that work at the base layer of the language. It includes many built-in rules and is used by many OSS projects. Another option is LibCST from Meta that preserves all the whitespace, inline comments, and code formatting which makes it good at complex refactorings. Then, like with JavaScript, there are framework-specific upgraders like django-upgrade for Django.

    Rust offers a built-in cargo fix tool

    Rust, like Go years earlier, added a native code modernizer in their toolchain. However, it’s quite different in how it works, despite doing conceptually-similar things.

    cargo fix uses rustc compiler diagnostics to detect deprecated features or edition-incompatible idioms to drive changes to source files. A foundational part of Rust modernization relies on Editions. Rust uses an “Edition” approach (2015, 2018, 2021, 2024) to introduce potential opt-in breaking syntax changes without breaking the ecosystem. cargo fix was introduced to automate code modifications needed to transition crates (packages) from one Edition to the next.

    I can’t run cargo fix to jump from a 2018 Edition to 2024. I’d execute a step-by-step path to first go to 2021, then to 2024. The tool’s goal is to modify code so that it’s valid in both the current Edition and target one simultaneously.

    Of note, there are more modernization options than just cargo fix. Clippy is the official code linting tool for Rust. It analyzes source code to catch any design mistakes and improve your code. cargo upgrade also upgrades crate dependencies, while cargo outdated shows you a detailed tree of outdated dependencies. Our friend ast-grep also applies to Rust and can help you with codebase-wide refactoring.

    Wrap Up

    Does any of this matter now that we have LLMs? Can I just point Claude Code, Cursor, or Google Antigravity at a legacy code base and ask it to shine it up? Sort of.

    Yes, you can use LLMs for some of this code modernization effort. But solely relying on an LLM? You’ll see a significant token spend, it’ll take magnitudes longer to execute a modernization, and you risk context truncation or degradation across massive files. It’s an “and” where you ideally couple LLMs with deterministic tools like some of the ones we listed here.

    What did I get wrong? Do you have other favorite tools that help you modernize code written in your favorite language?

  • Daily Reading List – July 29, 2026 (#835)

    Busy morning, quiet afternoon. I love having those moments to clear out the inbox, spend time reviewing docs, and even writing a blog post (for tomorrow). Do you freeze when you have an open afternoon without meetings?

    [article] How much can you delegate to agents? Is it easy to check? Is it cheap to undo? Ask yourself these questions (and see this flowchart) to help decide how much autonomy you should give your agent.

    [blog] Eval Is Evil: How to Safely Execute Untrusted AI Code with Cloud Run sandboxes and ADK. Running injected scripts in your app? Fun for demos, dangerous for real life. This post looks at how to sandbox untrusted code.

    [blog] We Killed Reading the Same Way We Caused the Obesity Epidemic. Reading is so good for you. We’ve rigged the game against ourselves, but this post offers a handful of ways to reset.

    [blog] Why care about programming languages. It’s not about the syntax; it’s about the deep ideas about computation manifested in these languages.

    [blog] Automated pull request reviews with Gemini Managed Agents. Nice example of using these fully managed agents.

    [blog] Gameplay Before Strategy. Caught up (frozen?) in high-level grand strategy, but missing the feel that comes from gameplay? Help your team stop hurting themselves, and then aim bigger.

    [site] Superlogical: We are building the multiplexer for all work. New startup from Mitchell, co-creator of Hashicorp. Looks like he’s solving a very relevant problem.

    [blog] Finally — Hard Caps to Limit Your Google Cloud Spend. Spending caps, in action. Lower your anxiety about finding out too late about spiking usage.

    [article] Target SVP says its real AI moat isn’t the models — it’s everything built around them. If the model itself is your moat, I think you made a mistake along the way. The models are great, but your advantage comes from what you build around it.

    [blog] Automate your agent development lifecycle using any coding agent. You can do most anything without leaving your coding harness. Here’s how you build, deploy, govern, evaluate, and share your agent.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below:

  • Daily Reading List – July 28, 2026 (#834)

    Anytime a new version of something comes out in the tech industry—LLMs, mobile phones, services—people start asking “what’s next” a moment later. I’m quite excited by all the stuff in our Google queue, but I’m more interested in what’s already shipped. Let’s use what we got before obsessing over things we don’t have.

    [blog] How AI Is Changing Open Source. More (random) projects, overwhelmed maintainers, and declining motivation to go to the effort of open sourcing? Sounds dire. Still reason for optimism.

    [blog] Rich UI in Gemini Enterprise: A Field Report on A2A Agents and A2UI Primitives. Is the future of software distributed agents that use dynamic UIs for user interaction? Possibly. Pay attention to generative UI developments.

    [article] MCP’s biggest update removes the machinery many servers were built around. A remote MCP server can now act more like a stateless HTTP service. This will require some changes, but should simplify things for many. More here, and here.

    [blog] Don’t Let Your Agent Mess Up Your Database. Speaking of MCP, this is a great solution to a missing feature of MCP.

    [blog] 3 things top 1% teams do differently. Your mileage may vary, but this post says that improving hiring, embracing parallelization, and adapting your code review flow are keys to success.

    [blog] Detect early and enforce firmly with Google Cloud’s enhanced cost controls for AI spend. We’re doing some good work to try and get you ahead of any runaway costs.

    [blog] How I set up containerized dev environments you can spin up in seconds for Antigravity CLI, Neat project. Isolate an agentic CLI into its own sandbox so that you can give it a limited set of rights before letting it go wild.

    [article] Friday Forward – Be Bored. Big fan of forced boredom. Especially for adults. Sit still. Just be. Give your brain a chance to make novel connections.

    [blog] Gemini API Managed Agents: 3.6 Flash, hooks, and more. Sweet update. Define your custom agent (prompt, tools, whatever) and have it run on a schedule in the cloud.

    [article] Our Favorite Management Tips on Setting Strategy When the Path Is Unclear. I liked these. Few of us work in boring, stable places. Are you effectively navigating the constant change?

    [blog] Merge Conflicts Are a Process Problem, Not a Git Problem. A merge conflict happens when Git doesn’t know which change should win. That’s a team problem to fix.

    [article] Why I write skills instead of agents for knowledge work. Don’t customize an agent or LLM when a skill will do. Karl outlines his current thinking.

    [article] Enterprises contend with mounting AI costs as tools sprawl. Not surprising. We’re in the experimentation stage where you’ve got to try a mix of tools before you consolidate. This post links to a series of articles on the topic.

    [blog] Vibe code your first Android app with Antigravity 2.0 & Android Studio. Wow, Alicia made this workflow actually feel approachable. I may try it myself.

    [blog] Google Cloud Skills Tutorial — Part 4 : Google Cloud Solution Architecture Skill. This scenario is exactly why agent skills are useful. Instead of relying solely on what an LLM knows (or what a search agent can scrape), feed your harness a skill that steers it to the most relevant cloud architecture.

    Want to get this update sent to you every day? Subscribe to my RSS feed or subscribe via email below: