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

richards2 pts0 comments

Which programming languages offer built-in tools for modernizing your code? – Richard Seroter's Architecture Musings

Skip to content

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

Written by

Richard Seroter

in

.NET, AI/ML, Go, Node.js

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 (<br>"flag"<br>"fmt"<br>"io/fs"<br>"log"<br>"os"<br>"path/filepath"<br>"strings"<br>"sync"

// Supported image extensions (Go 1.18 slice)<br>var imageExts = []string{".jpg", ".jpeg", ".png", ".gif", ".webp"}

// RenameRegistry ensures destination filenames are unique, resolving collisions thread-safely.<br>type RenameRegistry struct {<br>mu sync.Mutex<br>names map[string]int

func NewRenameRegistry() *RenameRegistry {<br>return &RenameRegistry{<br>names: make(map[string]int),

// GetUniqueName returns a collision-free filename in the destination directory.<br>func (r *RenameRegistry) GetUniqueName(base, ext, destDir string) string {<br>r.mu.Lock()<br>defer r.mu.Unlock()

count := r.names[base]<br>r.names[base] = count + 1

var targetName string<br>if count == 0 {<br>targetName = base + ext<br>} else {<br>targetName = fmt.Sprintf("%s_%d%s", base, count, ext)

// Double check disk existence to avoid overwriting existing files<br>for {<br>targetPath := filepath.Join(destDir, targetName)<br>if _, err := os.Stat(targetPath); os.IsNotExist(err) {<br>break<br>// If it exists, increment the counter and try again<br>count++<br>r.names[base] = count + 1<br>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)<br>func isImageFile(path string) bool {<br>ext := strings.ToLower(filepath.Ext(path))<br>for _, item := range imageExts {<br>if item == ext {<br>return true<br>return false

func main() {<br>// Parse CLI flags<br>srcDir := flag.String("src", ".", "Source directory containing photos")<br>destDir := flag.String("dest", "", "Destination directory for renamed photos (defaults to source)")<br>dryRun := flag.Bool("dry", false, "Dry run mode (lists proposed changes without executing)")<br>verbose := flag.Bool("verbose", false, "Enable verbose logging output")<br>flag.Parse()

// Default destination to source directory if not specified<br>if *destDir == "" {<br>*destDir = *srcDir

// Clean paths<br>*srcDir = filepath.Clean(*srcDir)<br>*destDir = filepath.Clean(*destDir)

log.Printf("Starting photorename CLI...")<br>log.Printf("Source directory: %s", *srcDir)<br>log.Printf("Destination directory: %s", *destDir)<br>if *dryRun {<br>log.Printf("DRY RUN MODE ENABLED - No files will be moved or renamed.")

// Scan source directory recursively<br>var photos []string<br>err := filepath.WalkDir(*srcDir, func(path string, d fs.DirEntry, err error) error {<br>if err != nil {<br>return err<br>if !d.IsDir() && isImageFile(path) {<br>photos = append(photos, path)<br>return nil<br>})

if err != nil {<br>log.Fatalf("Error scanning source directory: %v", err)

totalPhotos := len(photos)<br>log.Printf("Found %d photo(s) to process.", totalPhotos)<br>if totalPhotos == 0 {<br>return

// Ensure destination directory exists (unless dry run)<br>if !*dryRun {<br>if err := os.MkdirAll(*destDir, 0755); err != nil {<br>log.Fatalf("Failed to create destination directory: %v", err)

registry := NewRenameRegistry()<br>var wg sync.WaitGroup<br>// Allocate worker pool limit using custom new helper (pre-Go 1.26 newexpr target)<br>limit := newInt(4)<br>// Limit concurrency using a semaphore (buffered channel)<br>sem := make(chan struct{}, *limit)

var successCount int<br>var successMu sync.Mutex

for _, photoPath := range photos {<br>photoPath := photoPath<br>wg.Add(1)<br>sem %s", photoPath, targetPath)<br>successMu.Lock()<br>successCount++<br>successMu.Unlock()<br>return

// Perform the rename/move...

string directory destdir code return destination

Related Articles