making the roaming future

Chasing the vibe-coded bottlenecks

Three backends, three languages, and what the benchmarks taught me about my own assumptions.

I’ve been building Slips — a collaborative, real-time task list app, as part of a series of lightweight, self-hostable collaboration tools - with one constraint: the backend would be written primarily by an LLM, with me in the role of technical director rather than primary author. The result was three functionally identical server implementations: Node.js, Go, and Swift. Same API surface, same SQLite persistence, same WebSocket sync protocol. For the client apps, each backend must be essentially a drop-in replacement.

The experiment started as a productivity exercise. It turned into something more interesting: a systematic audit of where LLMs may fall short, what “idiomatic” code actually costs at runtime, and some wrong assumptions about language and runtime performance I had.


The Setup

Slips is a collaborative task list with real-time sync to multiple clients over WebSockets. The HTTP API covers the usual operations: create a list, fetch it by share token, manage tasks. The share token is derived via SHA-256 from a random base64 string, so every request that looks up a list performs a hash computation. The WebSocket layer handles live sync using the Automerge library, using CRDTs.

All three backends use SQLite as the persistence layer (WAL mode, which I’ll come back to), run on the same machine (Apple M1, macOS), and were benchmarked with a Go-based tool running 200 operations at 10 concurrent workers.

The Node.js implementation came first and received the most guidance - detailed prompts, iterative corrections, explicit direction toward correct behavior, and successful manual tests. The Go and Swift versions were reimplementations using some lighter prompting: “here’s what this does, look at that fugly js code, use the target language the way it was meant to be used.”


Round 1: Node.js — Getting it Working

The Node.js backend reached a working state relatively quickly. Express for HTTP, better-sqlite3 for synchronous SQLite access, ws for WebSockets. The LLM’s choices were reasonable and pretty conventional. Sounded pretty good with what I’ve been using in the past that were good enough defaults to not drop down too deep a rabbit hole of dependencies.

The first benchmark session revealed numbers that, on the surface, looked acceptable: around 1,400 ops/s on sequential POSTs. Not fast, but not obviously broken either. Given that at the time the benchmarking started, all three implementations already existed and were somewhat functionally equivalent.

Web Crypto surprise

The function that derives a list ID from a share token - called on every single request - was implemented using the Web Crypto API (crypto.subtle.digest). This is the modern, standards-compliant way to hash data in JavaScript. It’s also asynchronous, which means every request was performing a thread pool dispatch and a Promise task just to compute a SHA-256 hash.

The fix was switching to Node’s built-in crypto module, specifically createHash('sha256') — a synchronous C++ binding that goes directly to OpenSSL with no JavaScript overhead, no allocations, no Promises. Sequential POST throughput went from ~1,153 to ~2,848 ops/s. 2.5x improvement from one function call change. Yikes.

The same file had a secondary issue in token generation: a spread operator feeding into btoa() followed by three separate regex passes to clean up the base64 output - what in the holy convoluted batman is this. Replaced with a single randomBytes(32).toString('base64url') call. Same result, one native operation. I was quite surprised how convoluted the original implementation was. As if it just tried things until it made it work. After all, making this implementation was the most painful, and required most hand holding, since the requirements seemed not to be grasped in their entirety, even though we worked out the spec before the first code hit the runtime.

Logging overhead

At the default log level, debug output to stderr was consuming a measurable fraction of request time. With LOG_LEVEL=error, sequential POST throughput recovered to 2,975 ops/s — matching the historical best. This isn’t a criticism of the LLM; it’s a reminder that benchmarking with debug logging enabled is benchmarking the logger. On the other hand, the real world deployment requires logging, so benchmaxxing cannot drive such decisions. Maybe the logging layer needs some tweaks of its own.

SQLite WAL mode

The SQLite journal mode defaults to a rollback journal that serializes all reads behind active writes. WAL (Write-Ahead Logging) decouples readers and writers by appending changes to a separate file, letting readers operate against a consistent snapshot while a write is in progress. For a workload with 10 concurrent writers, this matters enormously.

The impact was measured directly on the Go backend (where it was easiest to isolate): WAL mode delivered approximately 5x improvement on sequential writes and 5.1x on concurrent writes. All three backends ended up with WAL enabled, though the mechanism differed — Go and Node.js required an explicit PRAGMA, while Swift’s GRDB library via DatabasePool enables it automatically. Why the default was not to enable WAL in the first place. Beats me.

Final Node.js numbers: 2,975 ops/s sequential POST, ~1,024 ops/s GET by token, 121.8 MB RSS, actual physical memory taken, at idle.


Round 2: Swift — Where My Assumptions Broke

I expected Swift to be the fastest of the three, possibly by a significant margin. The reasoning: ARC eliminates garbage collector pauses, Swift compiles to native ARM64 machine code with aggressive optimization, Apple’s frameworks are tuned for Apple silicon. The LLM chose Hummingbird 2 for HTTP (a solid NIO-based framework) and GRDB for SQLite (which handles WAL automatically via DatabasePool).

The first benchmark told a different story.

The actor hop

The initial Swift implementation modeled the backend with two actors: an API actor handling routing logic, calling into a Store actor for persistence. This is a natural way to think about the architecture in Swift’s concurrency model — separate concerns, separate actors, compile-time safety.

The cost: each actor boundary is a cooperative executor suspension. Two hops per request, each carrying a ~5–15μs overhead. The benchmark result was 139 ops/s on GET by token. Go was doing over 3,000 on the same test.

This wasn’t the LLM doing something wrong in any naive sense. The code was correct, it was idiomatic Swift 6, it compiled cleanly with strict concurrency checking. The problem was that “idiomatic” and “performant” diverged significantly at this particular boundary.

The fix was restructuring to a single actor hop: a Sendable class for the API layer (zero executor cost from route handlers) calling into a single Store actor for actual persistence. Throughput jumped to ~3,300 ops/s. The actor boundary is still there where it matters — around actual database writes — but the unnecessary intermediate hop is gone.

33 allocations for a hex string

The SHA-256 hash of a share token needs to be hex-encoded on every request. The LLM reached for hash.map { String(format: "%02x", $0) }.joined() — a common Swift pattern that looks completely innocuous.

It isn’t. String(format:) routes through CFStringCreateWithFormat, which means Objective-C bridging. For each of the 32 bytes in a SHA-256 hash:

33 heap allocations per call, all hitting the Obj-C autorelease pool. At 5,000 requests/second, that’s 165,000 unnecessary allocations per second.

The replacement: a pure-Swift lookup table mapping nibbles directly to ASCII bytes, writing into a pre-allocated [UInt8] buffer, then constructing the final string with a single String(decoding:as:) call. One allocation instead of 33. GET by token throughput improved +57%.

The same String(format:) antipattern appeared in token validation (using CharacterSet, which bridges to NSCharacterSet) and token generation (using replacingOccurrences(of:with:), which bridges to NSString). All three were converted to pure-Swift equivalents.

Going further: unsafe buffer tricks

After the lookup table fix, there were still two unnecessary allocations in the hot path: an intermediate [UInt8] buffer for the hex output, and a Data copy of the input token for the SHA-256 computation.

String(unsafeUninitializedCapacity:initializingUTF8With:) writes hex characters directly into the String’s internal storage, bypassing the intermediate buffer. withContiguousStorageIfAvailable reads the token’s UTF-8 bytes from its internal storage without a copy (Swift 5+ stores all strings as UTF-8 internally).

The microbenchmark result across 100,000 iterations on M3:

Version Time Allocations
Original (map + String(format:)) 36,844 ns/op 33+
Lookup table with [UInt8] buffer 7,467 ns/op 3
String(unsafeUninitializedCapacity:) 6,051 ns/op 2
+ no-copy UTF-8 input 5,617 ns/op 1

An 87% reduction in that function’s overhead, purely from eliminating Obj-C bridging and redundant copies.

Final Swift numbers: 5,049 ops/s GET by token, 8,620 ops/s concurrent POSTs, 56.1 MB RSS. Note: Swift’s concurrent throughput varies across runs — the NIO event loop showed elevated CPU usage (up to 576% at idle) in some sessions, which dragged down benchmark scores. Historical best was 14,030 ops/s on concurrent writes.


Round 3: Go — Standing on the Shoulders of stdlib

The Go backend benefited from a combination of LLM choices that happened to align well with Go’s strengths from the start: net/http for HTTP, mattn/go-sqlite3 (CGO) for SQLite, gorilla/websocket for WebSockets. Standard choices, well-trodden path.

The performance optimizations here were less about correcting structural mistakes and more about pushing a good baseline further.

WAL mode and CGO

Switching from modernc.org/sqlite (pure-Go, WASM-based SQLite) to mattn/go-sqlite3 (CGO, native SQLite library) improved GET throughput significantly — from ~3,608 to ~6,861 ops/s on token lookups — because the CGO version has access to the full, optimized SQLite C library. Combined with WAL mode, write throughput approximately doubled.

Per-token shard mutexes

The original implementation used a single sync.RWMutex protecting the entire provider state. Under concurrent load, all goroutines writing to different lists were still serializing on this one lock.

The fix was splitting into 64 shard-level mutexes, keyed by FNV-1a hash of the share token. The provider-level lock is now only held briefly for map lookups; the CPU-heavy work — Automerge operations, cryptography, SQLite writes — runs under only the per-token shard lock. Different tokens can write concurrently.

Results:

Metric Before After Change
POST seq (ops/s) 2,907 5,327 +83%
POST c=10 (ops/s) 9,122 15,677 +72%
GET by token (ops/s) 4,739 7,536 +59%

fmt.Sprintf vs hex.EncodeToString

A small but measurable optimization: the original DeriveListID used fmt.Sprintf("%x", h) to hex-encode the SHA-256 hash. fmt.Sprintf uses reflection internally to format its arguments. hex.EncodeToString(h[:]) is a direct memory operation with a single allocation for the output string. The difference is small per call, but measurable at throughput.

Final Go numbers: 6,866 ops/s GET by token, 12,122 ops/s concurrent POSTs, 44.5 MB RSS.


The Final Comparison

Tested 2026-05-25 on Apple M1 (arm64), macOS 26.0. All three backends in the same session, fresh starts, clean databases.

Metric Go Swift Node.js
POST list (seq) ops/s 4,370 3,301 2,975
POST list P50 latency 0.20ms 0.25ms 0.26ms
POST list (c=10) ops/s 12,122 8,620 2,479
GET by token (seq) ops/s 6,866 5,049 1,024
GET by token P50 latency 0.13ms 0.19ms 0.69ms
Memory idle RSS 44.5 MB 56.1 MB* 121.8 MB
Binary size 10 MB 17 MB ~350 MB†

*Swift RSS was lower in this session (44.5 MB vs Go’s 56.1 MB) but varies; historical range 27–56 MB.
†Node.js binary size includes node_modules.


What I Got Wrong

“Swift will be fastest”

The reasoning was: no garbage collector pauses (ARC handles memory), native machine code, Apple hardware. What I underestimated was the cost of Swift’s concurrency model. The actor system is genuinely powerful and its safety guarantees are worth having — but actor boundaries have real runtime cost, and the LLM naturally reached for the most “correct” structure without profiling implications in mind.

Beyond concurrency, the Obj-C bridging legacy is a trap for anyone who learned Swift before Swift 5’s clean break. The String(format:) pattern is in tutorials, in Apple’s own documentation examples, in thousands of Stack Overflow answers. It’s idiomatic — and it’s expensive in such hot paths.

Swift can be extremely fast. Getting there requires knowing which APIs are pure-Swift and which ones drop into Obj-C under the hood. That knowledge doesn’t come from reading documentation - it comes from benchmarking.

“Node.js will be dramatically slower”

The final gap on sequential writes is Go at 4,370 vs Node.js at 2,975 — roughly 1.5x, not an order of magnitude. On concurrent writes it’s worse (12,122 vs 2,479), but that’s a fundamental architectural constraint: better-sqlite3 is synchronous and serializes on the main thread.

Node.js’s strength is that when it can offload to C++, it does so efficiently. The native crypto module isn’t a JavaScript wrapper with overhead — it’s OpenSSL with a thin binding layer. The V8 engine has had decades of investment. The runtime isn’t slow; the question is whether you’re doing work in JavaScript or in the C++ layer it sits on top of.

Bravo to Node.JS for holding its own, even though being the icky slow Javacript it is said to run underneath.

“Go is a middle ground”

Go won across the board, often by a significant margin. What I didn’t fully appreciate was how well optimized Go’s standard library is. The SHA-256 implementation uses BoringSSL for fast hash calculation. The hex encoding uses direct byte operations with no reflection. The HTTP server is production-grade and heavily optimized - it’s Google’s baby after all. Goroutines are lighweight and fast.

The LLM went with stdlib throughout the Go implementation, and took some impressive wins. Go team’s religious approach to simplicity, efficiency, and constant performance tuning of both runtime and the library surely inspires us vegans of the server-side world to thrive for the same when using it.


Reflections on LLM-Assisted Development

The most interesting finding isn’t in the benchmark numbers — it’s in the pattern of where the LLMs went wrong.

In every case, the initial code was correct by conventional standards. The Swift actor chain passed strict concurrency checking. The Web Crypto call used the officially recommended modern API. The String(format:) hex encoding is in the Swift documentation. None of these were bugs; they were choices that looked right until you measured them.

LLMs optimize for code that looks right, reads well, and follows documented patterns. They’ve been trained on the entire corpus of human-written code, which skews heavily toward “working” rather than “optimal.” They don’t profile. They don’t have an intuition for what a particular abstraction costs at runtime.

What an experienced developer adds to this loop isn’t more code — the LLM handles that. It’s the mental model that asks “what is this actually doing at runtime?” when something looks clean already. It’s the habit of measuring before assuming. And, as this experiment showed, it’s the willingness to have your prior assumptions proven wrong by the numbers.

The Node.js version required the most guidance during development. It also revealed the clearest antipattern (Web Crypto async vs native sync) and delivered the cleanest optimization story. The more “autonomous” Go and Swift implementations had more interesting structural problems to untangle.

I’m not sure what conclusion to draw from that, except that “less hand-holding” doesn’t mean “better code” — it means the LLM made its own decisions, and some of those decisions were questionable.

Surely augmenting the system prompt for the coding environment, setting some proper guardrails and directions in agent instruction files, or using a set of advanced skills freely available for each ecosystem would steer the LLM in the right direction much faster, but the experiment was to see what the defaults are. And many of those defaults stem purely from the models’ knowledge cut-off, and those are a completely different story, a gradually becoming a much more depressing one.


Numbers Don’t Lie, But They Do Require Context

A few caveats worth noting:

So, AI agents are a thing now

Came a bit late to the whole AI agents fun, so I decided to write my own harness thing in Golang. The idea of it running with scissors around my filesystem was pretty scary, so I limited its file write capabilities to the workspace directory.

Only a handful of shell commands are whitelisted. The harness attempts to limit command output to the workspace directory too, and, as a bonus, pipes commands to avoid cluttering up the context. Still need to encourage tail/head/grep use for text files to further optimize I/O and context use.

Additional shell commands can be enabled by creating a skill that has to be verified and approved by the user using slash commands. Still figuring out if and how those commands could be prompt-injected, so still lots to learn there as well.

But seeing the thing figure out commands by searching them through the Brave API, finding required parameters, and start performing background or scheduled tasks that consist of natural language commands is really cool. And all that using a self-hosted model.

Started getting the hype.

Also can see how agentic workflows like that quickly expose weaknesses of small, sub-10B models. Those get confused really fast, forget commands, tools, and skills, or ignore skill details, go for default parameters, and focus on the wrong stuff.

But on a sufficient model, it all just works, and the ability to tell my model-wrapper through WhatsApp to download a YT video for me, and check the news for a specific topic is pretty rewarding.

Need to make it figure out more about the world, tap into audio/video feeds, and can start preventing crimes too.

Building like it's 1984: A comprehensive guide to creating intuitive context menus

Oh man, we’re in a desparate need of such instances of attention to detail.

Noticed some forks popping up of my Valetudo Homebridge plugin, since it’s become pretty out of date due to REST API changes in Valetudo and lack of time on my part. I released a new beta yesterday that actually works now. Sorry and thanks for the nudge.

I finally got to implementing app state restoration in Headlines. Hoo boy, there’s so little comprehensive info on how to implement it when app’s whole UI structure is implemented in code, especially when it involves UISplitViewControllers. But in the end, it’s magical when it works!

I find it a little bit sad that people working their asses off to make an app sold for $3 feel the need to take part in the Black Friday sale. The prices especially on the iOS market are already heavily discounted.

RIP Steam Controller. The best thing to happen to couch PC gaming outside of Steam Link.

Nice job, Apple and NVIDIA -- two of my favorite companies -- of making all of us lose, by being dicks to each other. But of them two it’s surely Apple that loses more by effectively limiting the eGPU hardware to one vendor.

Kind of hoping that it’s just NV forcing Apple’s hand for both to finally kiss and make up. Apple doesn’t even need to source NV GPUs for their computers, having been burned (haha) before — AMDs latest GPUs are completely fine. But having an option again to run CUDA on macOS would sure be nice.

Advertising is a cancer on society

It’s a good one

Enabling HomeKit compatibility on a rooted Xiaomi Roborock S50

I recently bought a Xiaomi Roborock S50 vacuum cleaner. On the surface, it sounds like a great product -- pretty affordable, makes for a good looking home appliance, and most importantly, good at taking over a mundane activity that should’ve been automated a long time ago.

On the software side, there is a pretty usable app that allows you to make the most of the device, like checking its current status, customizing some settings, such as cleaning speed, schedule, and several modes of cleanup - general one, spot cleaning, and with the use of the indoor navigation enabled by a laser sensor, zone cleanup, or setting up virtual barriers and no-go zones.

The app also enables you to control the vacuum when you’re away from your home Wi-Fi. It does that by taking advantage of the cloud, i.e. Xiaomi’s computers located in China.

That last thing creeped me out most about this product, and before making a purchase I made sure there’s a way to do something about that. Turns out, there is!

Rooting a vacuum cleaner is so 2019

The software is called Valetudo, and it is essentially a webserver that’s running on the vacuum locally, faking Xiaomi’s backend services. It is not the main piece of the puzzle, though, as putting that webserver on the device was enabled by the amazing work of a group of people behind Dustcloud, which brings a set of methods of opening up the device to hacking.

Installing Valetudo is a pretty simple procedure, and in the end, instead of a piece of software with an origin in a country of questionable respect for people’s privacy, you get a mobile-friendly web client served by that device as well. There is an option to run some of the stack on a home server, but that sounds like too much of a maintenance burden.

HomeKit it

As an Apple and home automation nerd, making all things HomeKit-enabled is important to me. The robot even with its original software is not compatible, but there are several plugins for Homebridge that connect both worlds. The thing is, those plugins don’t control the robot directly, but through the Xiaomi cloud. Once we freed the vacuum from it, we need to find another way. Valetudo with its locally-running web server is exposing a simple HTTP API, so there is a way to control it programmatically, but as niche Homebridge is, a rooted vacuum is surely an even narrower niche, although I can see an overlap in audience there.

There were some requests posted to the authors of the aforementioned Homebridge plugins to add compatibility with Valetudo, but rooting a vacuum seems to be too new of a thing, so in the end there were no plugins for Valetudo-enabled devices. Now that I’m recently also a Node.JS developer (huh?) making backends in Javascript (get off my lawn), I made one.

Make it

The plugin is called homebridge-valetudo-xiaomi-vacuum, it’s available through npm, and the basics are pretty solid already. It exposes a few cleaning modes as buttons, provides the battery status, and some other conveniences. And I had heck of a fun time making it.

Find function call sites in Xcode

Recently I have been asked a few times, mostly by disgruntled ex-iOS Android developers, whether Xcode does anything more with the source code than just color-highlight it or allow to jump around the sources by using Jump to definition.

One of the things people seemed to miss the most is the ability to find places which call the focused function, known as, among others, “Find calls” in other IDEs.

It’s true that Xcode has always felt more like a text editor with a compiler than an IDE that provides a rich user interface on top of the source code, like Eclipse or IntelliJ’s tools, for example.

Good news is, it does indeed have some cool code structure aware features hidden in the plain sight. This approach goes in line with Apple’s philosophy of a clean design that doesn’t overwhelm the user with available options. In case of a programming environment though, which is a specialized tool in hands of a perceptive and analytical user for several hours a day, such features could be perhaps a little more obviously visible.

And so there is an extremely useful Related Items editor menu, by default available under a Ctrl-1 shortcut, that includes, among others, a Callers submenu. Yay!

Of course, its contents become available once Xcode manages to index and process app’s source.

Xcode Calls

Enjoy!

Tom Cruise promoting disabling motion smoothing in TVs is something I did not expect.

Loved the New Mac mini intro video, reminiscent of That One Scene from The Last Jedi, with a cool Blade Runner-like audio background.

Finally managed to build working PyTorch and fast.ai with GPU support on macOS, and now my puny GTX 1060 is ready for some courses, phew.

The new 13-inch MacBook Pros are starting to look truly compelling. The keyboards seem to be fixed, all four Thunderbolt ports are full-speed, include crazy fast SSD. I’m ready to return my Escape 2016 as it’s broken by design. Thanks EU.

Sorry for the slight RSS feed screwup. I made a mistake of defining post and feed IDs as URLs containing blog site’s protocol. Had to rip that bandaid off.

Scratch that. macOS deserves more from apps than a remote rendered iOS UI ported to desktop.

Yay, Headlines for macOS coming probably “late 2019”. I did dabble in AppKit, but was too lazy to code the app’s UI. ;)

Your stuff on The Web in 2018

We want an open web? It needs to be a lot less nerdy. Read more…

If you happen to have issues resolving VPN-origin domains on a local network where your DNS server is a Tomato-powered router, make sure you have the “Prevent DNS-rebind attacks” option disabled.

MacBook Pro Escape 2016 first impressions

I didn't choose the dongle life. The dongle life chose me. Read more…

Tests are about design

Recommended post by Karl Seguin. I have come to the same realization pretty late in my development career, but at least early in the “tests are actually useful” phase. Before that I mostly relied on compiler’s work and somehow ignored the need to check the logic beyond syntactic correctness.

Oct 27 Special Apple Event made one more developer angry

And it's not about the full-on USB Type-C transition. It's watering down the Pro moniker. Read more…

Blogging workflow update

Loving the element that was missing in my blogging workflow — ability to post from my phone and have it published to the site automatically while keeping the static nature of content.

Thanks to Dropbox, DO Note and recently added features needed to blogger, which can now listen to post source directory changes, I finally connected the building blocks.

A* - A Truly Iterative Development Process

Nice post by Jason Gorman. It’s obvious, really, to focus on project’s goal. Tasks and features are secondary.

Surprised how much lint accumulated in my phone’s Lightning port. After some toothpick action the satisfying “click” is back!

Can we save the open web?

We need to. Our privacy is slipping away from under our fingers. And for what? Often times, to receive something “free” in exchange.

Keychain improvements in iOS 9

Recently the keychain on iOS has gained new capabilities in terms of security and privacy, especially around Touch ID. Let's explore some of those. Read more…

Headlines 1.4 released

I'm happy to announce that after months of tweaks, real-world testing and foot-dragging, version 1.4 of Headlines is finally up on the App Store. Thanks to a coworker and a friend of mine that happens to be an awesome UX and UI designer as well, I'm extremely happy with app's updated look. Read more…

It’s great how Swift’s flexibility and lightness allowed me to return to heavily protocol-oriented programming. I dropped that idea a few years ago when doing Objective-C projects as it was too cumbersome there. Cocoa APIs still need some protocol-aware treatment though.

Have been digging into web development lately for a side project, and boy did things change since early jQuery days!

Alphabet. Buy n Large of tech world.

Varieties of news readers

Brent Simmons puts news readers in three categories: Newspapers, mailbox-type, rivers. Read more…

You have to take pleasure in the little things. 😎

Chris Lattner retweeted

Application Transport Security override

iOS 9 will default to a new connection security mechanism called App Transport Security that essentially forces apps to transfer data from their backend services using secure HTTP connection practices, like TLS 1.2 for instance. Read more…

@macbirdie: Such a lost opportunity. Swift 2 has repeat-while, instead of repeat-until. 😢

Blogging easier

As the setup of my blog requires creating a markdown file that follows a special template, I kept finding excuses to not write anything, because I had to perform a few awkward steps:

It felt dirty and unelegant, so I implemented an option in blogger that prints out a specific template, be it a post or a snippet, which includes a header filled with time set to 15 minutes in the future, an example title and chosen author. My obsession for clean technical solutions to first-world problems is satisfied and blogging is fun again!

Passwords

What if we kept one master password and let our device generate an account- or site-specific passwords needed on-demand? It's definitely not a new idea, though I'm not entirely sure why not a very popular one. Perhaps there's a gap between the notebook/post-it and password database approaches I'm not aware of? Read more…

Welcome to 2015! After Headlines 1.1, 1.2 happened in December. And now, 1.3 is waiting for review with some great improvements. More details coming in a few days.

And here it is! Headlines 1.1 should become available during the next 24 hours.

Wow, the Apple review process seems to have crazily slowed down due to massive upgrade rush to iOS 8 and the new phones. More than 10 days after submitting, 1.1 was still stuck in “Waiting for review” status. Luckily (!) I found a crashing bug in the app, so I requested an expedited review and just got an email that the request has been accepted.

Decentralized

Posts by Manton Reece and Noah Read, as well as a recent Core Intuition episode 155 inspired me, among other things, to add snippets on my blog. Though it's a lot easier to switch to a Twitter app and simply tap away, Twitter owns whatever you write to the level of controlling the way it's allowed to be shared outside of the ecosystem. I.e. it's a platform that's open to content being added to it. Read more…

As a tangent to Pragmatic episode 38, I wonder if and when Apple will move to USB Type-C in Macs. For sure for a lot of people it will be either too late, or too soon.

Story timelines

Facebook's timeline algorithm has received a lot of heat in the last few months after it was revealed that the timeline was essentially a part of an experiment and users following the default "Top stories" timeline were the test subjects. Read more…

Contractually-obligated testing

I usually set up assertions in methods in order to guard correctness of incoming parameters, outgoing results and invariants, but this technique looks very interesting. Here the contract can be set up in one place and modified without changing the methods’ implementation.

Headlines app

So I made it to 1.0 and it was not easy. Read more…

Technical debt 101

Nice article summarising what technical debt actually is.

Compiler Writers Gone Wild: ARC Madness

ARC is magic, but sometimes, as with all magic, if one is not careful, it might become dangerous.

Hello world

So, you surely know about the whole shoemaker and his wife’s shoes thing. This has been a long time coming, but I finally got around to making a static site generator, like all the cool kids do these days.

I hope I will be able to find time and will to make good use of it.

func hello() -> String {
  return "Hello"
}

Hold on, that’s not Go. Looks like Apple cooked up something equally interesting. Good times!