Testing safe area insets on iOS

I’m adding edge-to-edge UI support in Redwood. My code asks the host platform how much of the screen is consumed by system bars and notches and things, and it returns us a measurement like ‘40 pixels at the top + 10 pixels at the bottom’. The iOS code to handle…

Bad Luck Tickets

My son’s swim meet is coming up. If he’s healthy, he’ll compete. My daughter’s hockey team has a game coming up. League rules say they must forfeit if they have fewer than 10 players. There’s only 10 girls on the roster so they need everyone…

Writing For Me

I’ve been posting on this website throughout my career. Many posts are motivated by my own suffering! These go like this: I learned a thing.The hard way.It sucked.Please avoid making the same mistake.In other posts, there’s a discussion on something and I feel compelled…

‘It Needs Documentation’

I’ve seen bug reports and Slack conversations that start like this: @blacktshirt1981: ‘I tried to use your library to decode the integer 1 to a boolean and it crashed. Please document that your library doesn’t decode integers to booleans.’I probe a bit in my response: @jessewilson: ‘Oh…

I Want a Fast Whitespace Fixer

A couple of us are grumpy that putting robots in charge of code formatting is too slow & too clumsy. Jake Wharton reminded us that ranting isn’t as useful as participating, and challenged us to work on fixes. Lemme start with my ideal: IntelliJ, Android Studio, and a command-line…

Jesse’s Presentations

This is a running archive of my technical presentations. I’ve also posted about my process, and I also follow Jake’s advice on conference talks. 2024GC You Later, Allocator 2022Nerding Out on Okio Dynamic Code With Zipline 2019JSON Explained HTTP in a Hostile World 2018Ok Multiplatform! Writing Code That…

GC You Later, Allocator

I presented this at Droidcon NYC on September 19, 2024. Available as code, video, slides, Droidtube, & Speaker Deck. Memory management on Android is easy: the garbage collector does almost everything and LeakCanary handles the rest! But Kotlin/Multiplatform brings new challenges. Your new features could be blocked if Kotlin/…

Identifiers aren’t Services

Originally published on May 23, 2024 in fun code(). The programs I write frequently involve strings that identify things: email addresses, file paths, URLs, time zones... even credit card numbers and drivers’ license numbers. For many years I followed the patterns of the Java standard library when creating my own…

Get a Version Name for a Commit

In some of the projects I work on, we publish an artifact for every build on the main branch. We don’t want to give these nice semver names like 1.5.0 because that’s too much work! Instead we just wanna generate a version name based on the…

Pair More

I pair program a lot and it makes me happy. Here’s some thoughts on pair programming... Tuple is AmazingI’ve tried pairing with Copilot, Screenhero, Drovio, Zoom, macOS Screen Sharing, and Code With Me. Tuple is my favorite of these. Not Just ProgrammingI use pairing software for all kinds…

Open Source & Zelda

People do open source for lots of reasons. One reason that I do open source is because I enjoy it... Recently I had fun connecting Okio to zlib. It even had fun side quests: I learned about zlib, info-zip, PKZip, and how they relate. Sometimes a project’s logo tells…

Decisions, Deciders, and Executors

Most of the software I write exists to automate a process. It might automate a high-level workflow like peer-to-peer money transfers, or a low-level implementation detail like encoding an HTTP request as bytes. I’ve learned that my code is healthier when it’s strictly divided into value objects, service…

A non-scientific survey of ZIP Metadata

Okio's has an openZip() function that views a .zip file as a FileSystem. To expose file metadata in the file system API, Okio must extract it from the .zip file. There’s at least four ways to encode timestamps in a .zip file. I wanted to find out which of…

Honor and Internal Visibility

I don’t do many crimes. I don’t even do crimes that I could do without getting caught! Every single time my dog poops on the sidewalk, I pick it up. Even if nobody’s watching! I have honor. With that disclaimer out of the way, here’s instructions…

WebAssembly is like JSON for behaviour

If you’re sending data from one computer to another, it’s probably JSON. But it hasn’t always been this way. How we Transmitted Data Before JSONBefore JSON got popular (2005ish) we had XML, CORBA, Java serialization, Python Pickle, and a bunch of other clever and complex encoding schemes.…

Preparing for Network Failures this Holiday Season

Suppose I’m connecting my home Christmas lights to the Internet. Perhaps I’ll make a mobile app that calls my home control server via an HTTP API: POST /lights/toggle HTTP/1.1 { "subjects": ["maple_tree", "roof"] }HTTP/1.1 200 OK { "toggled": true }It works. I can finally…

Your Company’s DanceService probably doesn’t Dance

I work on a big product at a big company and we have lots of backend services that have big responsibilities: The messaging service sends customers their emails, SMS messages, and push notifications.The banking service manages the customers’ bank accounts.The identity verification service verifies customers’ identities.Except that…

Farm or Grind

Suppose your organization has a widely-used internal library for validating customer usernames: private val usernameRegex = Regex("[a-z]{2,40}") fun isValidUsername(username: String): Boolean { return usernameRegex.matches(username) }The function becomes widely adopted: Some callers use it during customer sign-up. We don’t want customers putting slashes in their usernames!…

Read a File in a Kotlin/Multiplatform Test

I like writing unit tests that read test cases from a data file: Zipline does ECDSA and EdDSA signing. It gets test data from Project Wycheproof.OkHttp does URL parsing. It gets test data from The web-platform-tests Project.OkHttp-ICU does Unicode Normalization. It gets its test data from Unicode’s…

Bob Bent the Universe

Bob Lee was murdered on April 4 in San Francisco. I met Bob in 2006 when I joined him on the AdWords front end (AWFE) team at Google. He was charismatic, inventive, and kind. With Kevin B. he created Guice, a dependency injector that we could use to make AWFE…

Surprised by Re-entrant Code

Some of the trickiest bugs I’ve seen happened because a function unexpectedly occurred multiple times on the call stack. Re-entrant ListenersTo illustrate, consider a TextField widget that implements the listener pattern: class TextField { var listeners = mutableListOf<Listener>() var value: String = "" set(value) { val previous = field if (value…

Factory Factory

I use lots of factory classes in my code. But not everywhere. I don’t like the ceremony they require to create and use: why not a constructor? Factories don’t solve a business problem directly; they solve an architecture problem. They feel enterprisey and architecture astronauty. /** * For example, a…

Code Changes Should Be Small or Mechanical

Easy code changes are additive: introduce a new database table, network call, or screen. Difficult changes are transformative: split a database table, combine a few network calls, or change a component that’s used on many screens. These changes tend to motivate refactoring: Introduce feature flagsExtract interfacesMove code between modulesAdd…

Optimization is Specialization

One aspect of programming that’s more art-than-science is making things efficient. I love finding shortcuts that make a system work better! It’s particularly satisfying to gather insights that we can exploit! I’ll illustrate with an example. Optimal for TodayHttpUrl is a class I wrote that implements URL…

Induced Demand & Code

This post expands on a section in my Writing Code That Lasts Forever talk. Here's what it takes to release a new version of OkHttp: Confirm the code is in a releasable state!Name the version (5.0.0-alpha.10 ?, ick)Summarize what’s new in the changelogTag the release…

Dynamic Code With Zipline

I presented this with Jake Wharton at Droidcon NYC on September 1, 2022. It’s part one of a 2-part presentation with Native UI with multiplatform Compose. Available as video, slides, Droidtube, & Speaker Deck. As products grow, teams tend to move business logic to the backend. Keeping clients dumb…

Selection Bias in Hiring & Promotions

Let’s make a fictional company that employs workers in two levels: Senior worker: does important work; gets paid $50/hour.Principal worker: trusted with the most important work; gets paid $100/hour.Unfortunately, it’s difficult to differentiate the two levels! If a project completes early it might because…

Jesse’s Slide Decks

I presented Nerding Out on Okio at Android Worldwide last month. After sharing the video link and slide deck, Andrew Kelly tweeted asking if I’d share my process. I’m flattered by the question! So here I’ll indulge and share what works for me. The gist of my…

EventListener is Like Logging, But Good

In 2013 I opened OkHttp Issue #270, ‘Analytics API’. The request was to just add some debug logging: We should add verbose logging for the response cache. Folks occasionally are puzzled when the cache doesn't work. Let's make it easy for them to figure out why.We didn’t add…

Nerding Out on Okio

I presented this at Android Worldwide on April 19, 2022. Available as video, slides, YouTube, & Speaker Deck. Quirks and features of the I/O library that powers OkHttp.…

Uncertainty in Tests

I’ve been working on OkHttp’s Happy Eyeballs and exploring testing strategies along the way. Happy Eyeballs is the fun name of RFC 6555, which is a clever hack to deploy IPv6 even if some client’s IPv6 connectivity is unstable. Here’s how it works: A client has…

How to Manually Check a Maven Signature

Gradle has powerful features to check signatures of downloaded artifacts. If you want to check that things are signed right, that’s the best place to start. But if you wanna verify an artifact’s signature manually, here’s how... Find the signer’s keyThere’s a bunch of key…

Compiler Warnings Are Good, Actually

Every so often an exasperated teammate recommends enabling -Werror in all our repos. The reasoning is sound: Compiler warnings are bad.Having lots of them is demoralizing.If we had prevented them from ever occurring we wouldn’t be in this mess.This is a particularly good policy to prevent…

Okio FileSystem Update

I’ve been working on a file system API for Kotlin Multiplatform. It’s simple & fast and I’m excited about it. We’ve got these: FileSystem.SYSTEM: the local file system.FakeFileSystem: our in-memory implementation. Use checkNoOpenFiles() to be confident that your code doesn’t leak!ForwardingFileSystem: like…

Big Things Aren’t Just Bigger Versions of Small Things

I’ve been working on a product that’s grown from 5 developers in 2013 to 400 developers today. Our product complexity and customer numbers have grown similarly. I’m lucky and thankful to have been on a team whose hard work has paid off so much. But I’m…

Discovering Limits

All systems have limits. If I don’t design for them that doesn’t make them disappear! It just means I’ll be unprepared when they’re reached. Most of the systems I work on accept HTTP requests, perform operations on databases to satisfy them, and format their responses. Though…

Gradle’s includeBuild() is Awesome

I’ve worked on teams that use many repositories and teams that put everything into a giant monorepo. But no matter how much code we jam into git, there’s always something else on the outside. Gradle’s includeBuild() feature smashes multiple projects together into one. Recently I used it…

I Love Control Flow

As discussed previously, I’m working on a file system API for Okio. I’ve already written real & testing implementations, and now I’m adding ZipFileSystem: it views a zip archive as a read-only file system. Most of the work is reading specs, writing tests, and learning the gotchas…

NullPointerException: bio == null

Sometimes multiple threads close the same Socket concurrently. Unfortunately, Android 10 and 11 may crash when this happens: java.lang.NullPointerException: bio == null at com.android.org.conscrypt.NativeCrypto.SSL_pending_written_bytes_in_BIO() at com.android.org.conscrypt.NativeSsl$BioWrapper.getPendingWrittenBytes() at com.android.org.conscrypt.ConscryptEngine.pendingOutboundEncryptedBytes(…

Files, Boilerplate, and Testability

I’m working on Okio’s multiplatform filesystem API. It introduces 3 main types: Path: a value object that identifies a file or directoryFileMetadata: a value object that describes a file or directory, including its type and sizeFilesystem: a service object to read and write files and directories Plus two…

On Files and Okio

Though Okio has been multiplatform for a year, it still lacks support for reading & writing files. I’ve been thinking about what application developers need and what APIs we should build. But the more I consider it, the more I think, ‘fuck, filesystems are awful’. Files as UIComputing used…

Building on the Wrong Abstraction

Writing sturdy software is hard work. Sometimes it’s very hard work, but it doesn’t need to be. Very Hard Problem: ORMI have a confession: I like Hibernate. The @Version feature is my favorite; it makes optimistic locking easy and safe. I also like how simple it is to…

Many Correct Answers

Let’s copy a file with bad Java I/O: private void copy(File source, File target) throws IOException { try (InputStream in = new FileInputStream(source); OutputStream out = new FileOutputStream(target)) { while (true) { int b = in.read(); if (b == -1) break; out.write(b); } } } Whoops, it’s super slow. Reading a…

Optimizing ‘new byte[]’

How much time does it take to allocate a byte array? Let’s write a JMH benchmark: @Param({"1000", "10000", "100000"}) int size; @Benchmark public void newByteArray() { byte[] bytes = new byte[size]; bytes[bytes.length - 1] = 'a'; }Running this on my laptop shows that time scales with size: size…

My Best Money Advice

Managing money is work. In addition to the global meltdown that’s going on I must continuously optimize my cost of living, make sure I’m earning enough, avoid taxes, save for retirement, and donate generously. Plus the mild anxiety of whether I should keep HBO and Netflix and Disney+…

Bright Lines

I’m often working on some kind of self-improvement: changing my diet, exercising right, or adjusting my work-life balance. But changing my behavior is difficult and I had many failures before any successes. In 2012 I tried to reduce the amount of meat in my diet, from 1-2 times per…

X.509 Certificates in Kotlin

I learned most of what I know about TLS certificates through reading APIs, source code, and specs. My instincts from dealing with ASN.1, X.509, and TLS as a user is that it the APIs are complex, and I should stay away️ from the even more complex implementations. But…

Christmas Breaks

Deploying early & often is addictive. Frequent deploys make it easier to identify regressions, tighten feedback loops, and get fixes to customers sooner. So I’m happy that the Cash App team has tests & tools for frequent deploys. All this deploying has a catch: we don’t notice slow…

Synonyms are Bad

I’ve been working on TLS lately. The specs, the APIs, and even the docs make me feel dumb. Why is this so hard?! I struggle when it takes 2 hours to do something that should take 20 minutes. One thing that makes TLS difficult is the jargon and acronyms.…

Sandwiches & GC Pauses

Being a software developer has changed how I understand lunch. SandwichesOn Saturday I made myself a sandwich, and then my kids wanted one, and then my partner wanted one. For each: Get bread, toppings, knives, plates, cutting boardWash veggies, slice cheese, assemble sandwichDeliver sandwich!Return unused toppingsWash & return dishesMaking…