whoami
Lucas Jenß
cat /etc/motd
The Coding Journal ツ — Notes taken on an epic coding journey. Technical solutions, debugging notes, and practical guides from the trenches of software development.
ls -la ~/languages/
- drwxr-xr-x
- ▶ PHP
- ▶ Ruby
- ▶ Scala
- ▶ C#
- ▶ JavaScript
- ▶ Objective-C
- ▶ Shell Scripting
ls -la ~/toolchain/
- drwxr-xr-x
- ▶ Typo3
- ▶ Akka
- ▶ Capistrano
- ▶ Git
- ▶ MAMP
- ▶ Adobe Illustrator
- ▶ NSTrackingArea (Cocoa)
uname -a
- drwxr-xr-x
- ▶ Mac OS X
- ▶ Unix
Sending Akka case classes without serialization breakage
Akka serialization issues when sending case classes over the wire usually begin at the boundary between an actor system and the network. A local actor exchange can look deceptively simple: one node sends an immutable case class to another actor, and both sides refer to the same Scala type. During remote delivery, that object becomes bytes, crosses the transport and must be reconstructed by the receiver. A mismatch in serializers, class definitions or dependencies then turns an ordinary message into an opaque failure.
The useful approach is to trace the message through that boundary rather than change code at random. Start with the exception and the class name, confirm how the payload is encoded, then compare the sender and receiver classpaths. Every step should produce evidence that narrows the fault.
Understand the failure boundary
Local delivery can bypass serialization when the recipient lives in the same actor system. Remote delivery cannot: the sending side selects a serializer, converts the message to a byte array, and the receiving side must locate the matching class before constructing it. That makes an apparently harmless refactor, such as renaming a package, capable of breaking communication between nodes.
Symptoms usually fall into a few groups. A failure while writing the payload points to an unsupported value or a serializer configuration problem. A ClassNotFoundException on receipt indicates a missing or differently named class. An InvalidClassException often signals incompatible Java-serialized definitions, while a Jackson UnrecognizedPropertyException can reveal a changed JSON contract.
Record the full stack trace, selected serializer, fully qualified class name and versions of the shared JARs on both nodes. My miscellaneous fixes notes cover smaller environment quirks, but this failure needs enough evidence to separate encoding trouble from deployment drift. Australian teams frequently span Sydney, Melbourne, Brisbane or Perth, so “it is working here” may refer to a genuinely different runtime.
Build a stable wire contract
Treat the case class as part of a wire contract, not merely an internal convenience. Keep its package name stable, place it in a shared module and record an explicit serialVersionUID when Java serialization remains enabled. Adding a field can alter the automatically generated identifier, so compatibility should be tested rather than assumed.
For JSON, decide how absent values, unknown properties and defaults behave before deploying. Jackson with Scala support handles immutable case classes, Option values and Scala collections, but its mapping rules still need to match the intended schema. For sealed traits, use a deliberate discriminator instead of an unconstrained Any field, otherwise two nodes may interpret the same payload differently.
Schema-first formats make evolution more visible. Protobuf keeps field numbers meaningful; Avro can resolve a new writer schema against an older reader schema. JSON remains convenient for inspection and mixed-language consumers, while binary schemas suit high-volume internal traffic. In Australian banking, logistics and government services, the operational cost of maintaining that contract often matters as much as raw throughput.
Configure serialization deliberately
Classic Akka remoting commonly falls back to Java serialization unless configured otherwise. On an existing system, set akka.actor.warn-about-java-serializer-usage = on to reveal the dependency, register an appropriate JSON or schema-based serializer and bind the relevant message classes to it. Once the migration is complete, akka.actor.allow-java-serialization = off can prevent accidental fallback.
The exact configuration depends on the Akka version, but the order is consistent: install the serializer library, define its configuration, apply the binding and restart both actor systems. Both nodes must also resolve the expected domain classes. A serializer on the classpath does not make a renamed or absent case class available to the deserializer.
Keep payloads limited to data that can survive a process boundary. Live actors, futures, streams, database sessions and file handles do not belong in a message envelope; represent collaborators or identifiers instead. A broad Any value may also conceal a wire contract that different nodes interpret differently. When a library’s error handling is opaque, Mike Patt is another programming reference I keep open alongside the source.
Test across real network conditions
A round-trip test inside one JVM is necessary but insufficient. Exercise two actor systems, first on separate ports and later on separate hosts or containers, using the same dependency set as production. Verify that the receiving node actually loads the intended class; a stale JAR or shaded artifact can make a corrected build appear unchanged.
Add compatibility tests with two application versions running together. Send known messages in both directions, assert the decoded values and store a small payload fixture that reveals encoding changes without exposing sensitive records. During rollout, keep old and new readers capable of accepting the agreed format until every node has migrated.
Australian conditions add useful stress to the story: an NBN connection may be excellent in one office and less reliable in another, while hosted workloads can sit in Sydney or Melbourne as developers test from elsewhere. A common Australian aside—“it works on my box”—is a clue, not proof. Correlate exceptions, node versions and timeout data before increasing timeouts, since a retry can also duplicate side effects.
Compare the main encoding choices
There is no universally best encoder. Compare candidates against the actual failure modes in your system rather than benchmark numbers alone. Java serialization offers a familiar baseline but carries brittle class compatibility and unsafe deserialization concerns. JSON is readable and easy to integrate, although payloads are larger and mapping choices matter. Kryo can be efficient, while Protobuf and Avro provide explicit schemas but require contract discipline.
| Encoding | Main advantage | Typical risk | Strong fit |
|---|---|---|---|
| Java serialization | Minimal setup and native Java compatibility | Class evolution and deserialization safety | Controlled, short-lived internal deployments |
| JSON with Jackson | Readable payloads and broad tooling | Mapping drift and additional bytes | Mixed-language services and modest volumes |
| Kryo | Compact output with custom serializers | Registration and compatibility changes | High-volume controlled JVM traffic |
| Protobuf or Avro | Explicit schemas and strong evolution rules | Contract tooling and rollout overhead | Stable service boundaries and high throughput |
Security and operability narrow the choice further. Disable Java deserialization for untrusted traffic, keep actor-system authentication and transport encryption in place, and avoid logging complete business messages. A binary format may reduce bandwidth, yet base64 encoding and larger schema bundles have their own costs. Test rolling upgrades with the selected format before committing to it.
Make the fix durable in production
Before the next release, turn the investigation into a repeatable deployment check:
- Capture the failing class name, fully qualified package, selected serializer, payload shape and complete exception from both sending and receiving nodes.
- Compare shared dependencies and configuration, including
serialVersionUID, mapper rules and schema versions; identical deployment scripts do not guarantee identical runtimes. - Run the mixed-version test and a transport test against separate nodes, then verify invalid messages fail cleanly rather than disappearing behind a generic timeout.
- Roll out in stages, watch serialization failure rates alongside latency, and retain a rollback path until the older application version no longer receives traffic.
A durable fix needs both code and evidence. Move the message contract into a versioned shared module, record why that encoding was chosen and add the reproduction to the regression suite. Start with one failing message today: capture its details, rerun the compatibility test and publish the verified fix through The Coding Journal.
cat ~/interests.json
| Key | Value |
|---|---|
| editor | Terminal-first workflow |
| os | Mac OS X / Unix |
| vcs | Git, distributed version control |
| deploy | Capistrano, cron automation |
| graphics | SVG, Adobe Illustrator troubleshooting |
| networking | IP validation, SSH, VPN |
git log --oneline --reverse
Solving SVG import issues in Adobe Illustrator CS6 and CC
When importing an SVG into Illustrator, the operation fails with an unknown error [CANT]. A workaround for this Adobe-side bug.
Solving NDK build issues on OS X
Troubleshooting native development kit compilation problems on Mac OS X.
Programmatically adding PHP generated TypoScript to the backend configuration
Integrating dynamically generated TypoScript into Typo3 backend setups using PHP.
ArgumentError: Could not parse PKey: no start line
Debugging an SSH key parsing error encountered during deployment.
Validating IP-Addresses in PHP
Using PHP filter functions with flags like FILTER_FLAG_IPV4 and FILTER_FLAG_IPV6, and understanding how filter_var handles reserved IP addresses.
Cocoa: Using NSTrackingArea
A short tutorial on using Cocoa's NSTrackingArea to capture mouseEntered and mouseExited events.
cat ~/contact.txt