Making of a shared path

This is an entry in my series Things I learned. Here I learned the value of sending many emails and not to be afraid of dog poop.

In December 2017 we moved into our current apartment. Back then, there was a path nearby, that looked promising, but ended after 5 meters. There was a sign covered by a black plastic bag. After some years, the bag had fallen apart and revealed a sign for a shared pedestrian and bike path:

Photo of the 5m of unfinished path, used by 2 cars for parking.
Note the tree and shrubs behind the white van – nothing there indicating a path.

If this path would exist, we could use it as a shortcut, saving 250m on foot or bike:

Google Maps screenshot showing two connections: 350m around the north, 105m going straight.
Google Maps screenshot showing two connections: 350m around the north, 105m going straight.

In the summer of 2021 I asked my contact in the city administration (Stadtverwaltung), who’s involved in planning bike infrastructure in our district, about this. At first he said that due to a school being planned in the area to the north (the grey area in the screenshot above, inside the blue dotted path), he couldn’t give me details, but forwarded the question to the responsible department.

Half a year later, we didn’t have any update and nothing was being built. But by coincidence, a friend and me found some plans for this city quarter from 2003 (19 years old!) where this path was already included:

City planning map from 2003 indicating the path

That was in January 2022. It took a bunch more emails and another one and a half years, till August 2023, that the construction actually started. By then the new school had already opened.

Another photo of the same path, with a parking forbidden sign next to the pedestration/bike path sign and a digger at the other end.

I found it somewhat amusing that the first thing they did was to damage the poor blue sign that had been standing there uselessly for 6+ years. At least it had the company of this parking forbidden sign, which had seen things:

A badly scratched parking forbidden sign, in detail

By October 2023 they even finished the 3 lamps:

The finished path, with some street lights visible on the right of it, partly covered by 3 trees.

When the path was finally done, we started using it quite often. A year later in August 2024, my son started going to school. And since he went to the new school to the north of the path, we’ve been using this path twice daily.

Part 2

Since I feel like having some responsibility for this path, and use it so often, I noticed a growing problem: While many people use this path, some drop their trash there, which mostly ends up in the shrubs on the side. And people walk their dogs here – and some leave their dog poop behind, on the path.

My first attempt at fixing this problem was a trash collection done with some kids who live close by or go to the same school. In November 2024 I registered this as an AWB (the city organization usually responsible for keeping the city clean) Putzmunter event, which at least ensured that the trash bags we filled would get picked up the next day. I borrowed some tools from KRAKE, who also provided me with gloves and trash bags (I could’ve picked those up from AWB, too). With about 15 people, from toddlers to grandmas, we collected trash along the path and around the school, filling three trash bags. We also collected pieces of a TV and a can of nitrous oxide (laughing gas):

Photo of a lamp post, trash can and 3 filled trash bags next to them. There's also pieces of a small TV and small gas can.

But this didn’t have any lasting effect. AWB kept ignoring the path. Trash started collecting again, a week after our cleanup. And once some dog poop was on the path, more showed up quickly. There were no trash cans at either side.

What do to? Send more emails! This proved successful again. Someone admitted, maybe by accident, that AWB wasn’t responsible for cleaning this path. When I asked a few times who then IS responsible, eventually they let me know that they’re going to take the responsibility from January 2025 onward. By early February they finally cleaned up the leftovers from New Years. They also installed trash cans on both sides of the path, one with dog poop bags:

Photo of the shared path sign and a new trash can attached to the sign post.

Unfortunately this did not solve the dog poop problem. That kept showing up, mostly on the right side of the path, next to the shrubs. My contact at AWB didn’t have any recommendations for dealing with this.

One day I finally decided to try some direct action: I found a sturdy enough stick and pushed five piles of dog poop into the shrubs. And for about a week the path was dog poop free!

My theory for now: There is a breed of dog owner that’s willing to pick up their dog’s shit as long as everyone else does it. But if there is already dog poop lying around, they’ll just leave theirs as well. “Why should I clean if others don’t?” would be their rationale.

I’m planning another cleanup event once spring is arriving for good. We’ll then have to instruct every helper not to step foot into the poop-hiding shrubs…

PS: If you’re a dog owner or have dog owner insights, let me know if there’s a more sustainable tactic.

Things I learned: some 90s arabic pop

A few days ago I was in one of my favorite locations in my city quarter of Köln, Café Casablanca. While drinking my green tea with Morrocan mint, I noticed a song and especially it’s very catchy hook. I used my phone to give me a title and saved it for later.

Later at home, my wife, son and me listened to it together:

I didn’t know the artist. Here’s what Wikipedia has to say about him:

Khaled is one of the most important musicians in the history of Raï music in his native Algeria and is one the world’s best-known Arab singers.[2] To date, Khaled has sold over 80.5 million albums (10 diamond, platinum, and gold) worldwide, making him one of the bestselling Arabic-language singers in history. Among his most famous songs are “Aïcha“, “Didi“, “El Arbi”, “Abdel Kader“, “La Poupée qui fait non“, “Wahran Wahran”, “Bakhta”, “C’est la vie“, and “Alech Taadi”.

He holds the Guinness World Record for best-selling artist of raï music.

Wikipedia

To lower our ignorance a bit further, we figured we could listen to his most famous song, Aicha:

When else should I check out, before I go back to listening to https://naturheilpraxis-hauri.ch/ href=”https://obscura.bandcamp.com/album/a-sonication”>OBSCURA?

Things I learned: transpiling React Native JavaScript with babel in 2025

At work we’re using React Native to build our Android and iOS app. We use it with Hermes, a JavaScript engine Facebook/Meta built for React Native, which doesn’t have a JIT compiler, but is instead optimized for fast startup, by compiling JavaScript to a type of bytecode during build time.

Hermes supports plenty of modern JavaScript features, yet any current React Native app will have its source code processed by a bunch of babel plugins, transforming modern JS code into not-so-modern one.

In our case this bothered me in particular when debugging events handlers that make use of JavaScript’s async/await feature. Hermes supports this natively, but one of the babel plugins included in the build process, would replace each instance with generators (also a relatively new JS feature, but not quite as new as async/await). The resulting stacktrace, if not properly mapped to the original source code, is much harder to read, since there’s usually two extra items in the stack, one referring to a function injected by babel called _asyncToGenerator.

Trying to stop this transform from happening turned into quite a deep rabbit whole. I’ll summarize here what we learned:

  1. our babel.config.js uses babel-preset-expo as the only present
  2. babel-preset-expo in turn uses @react-native/babel-preset as a preset
  3. @react-native/babel-preset includes @babel/plugin-transform-async-to-generator as a plugin
  4. @babel/plugin-transform-async-to-generator itself transform async functions to generators!

To figure out this chain of dependencies, npm ls was useful:

npm ls @babel/plugin-transform-async-to-generator
└─┬ [email protected]
  └─┬ [email protected]
    └─┬ @react-native/[email protected]
      └── @babel/[email protected]

None of these presets have any configuration options we could find, medizinrezeptfrei24.de so in the end we decided to add another patch with patch-package:

diff --git a/node_modules/@react-native/babel-preset/src/configs/main.js b/node_modules/@react-native/babel-preset/src/configs/main.js
index 077e1a6..d27d775 100644
--- a/node_modules/@react-native/babel-preset/src/configs/main.js
+++ b/node_modules/@react-native/babel-preset/src/configs/main.js
@@ -133,7 +133,7 @@ const getPreset = (src, options) => {
     extraPlugins.push([
       require('@babel/plugin-transform-async-generator-functions'),
     ]);
-    extraPlugins.push([require('@babel/plugin-transform-async-to-generator')]);
+    // extraPlugins.push([require('@babel/plugin-transform-async-to-generator')]);
   }
   if (
     isNull ||

This is now the 10th patch we’re maintaining, despite using renovate for greenkeeping.

After we verified our app still works fine without this transform, I figured I can at least ask if this could be upstreamed to @react-native/babel-preset. In short: no, at least not yet. From the community discussion post:

  • @react-native/babel-preset needs to support JSC (ie Safari on iOS >= 15.4, and the version of JSC we build for Android) as well as Hermes, so support needs to be universal before we remove plugins completely, but we can gate individual plugins by target (isHermes).
  • There may be runtime performance implications, we can check with the Hermes team or run an experiment case-by-case on that.
  • Static Hermes is coming soon-ish, which will have a very different level of language support and performance characteristics, so it might make sense to hold off for that.
discussions-and-proposals, Remove @babel/plugin-transform-async-to-generator from @react-native/babel-preset

Maybe in a year or so.

If you liked this post, boost it on Mastodon. Please reply there with comments, questions or ideas.

Decision Record: Mocking in Jest tests

This is a lightly edited copy of an Architecture Decision Record I wrote at work and share here in public.

Date: 2023-09-06

Background

We use a lot of mocking in tests written with Jest whenever modules need to leave the JavaScript system barrier. That is, whenever we make network requests, access the location, microphone or camera or some other device interaction, we have to mock those interactions. The rest of this document will call these “system interactions”.

The approach to mocking system interactions currently varies a lot, since we have no convention or agreement around these.

Reasons for Decision

To make Jest tests easier to write and read and increase consistency, we should decide on guidelines for mocking system interactions.

Options

Option 1: Focus on unit testing

Whenever a module (a React component, some other functions) depends on other modules that have system interactions, we mock those direct dependencies, so that the module under test only depends on pure functions or mocks. We mock as closely as possible to the tested module. That could mean jest.mock('@tanstack/react-query') (when testing a custom hook using react-query) or jest.mock('../hooks/useGetUserData') (when testing a component that uses a custom hook).

Since we mock a lot of our own code, we’ll use Manual Mocks correctly to reuse mocks and avoid duplicating mocks many times. Relates to ‘Bonus option 2’ below.

This doesn’t mean that we would mock all dependencies of a module – that would lead to an excessive amount of mocking with little additional value. The focus is on mocking those dependencies that directly or indirectly involved system interactions.

  • Pros:
    • Tests focus on one module and don’t break when dependencies change
  • Cons:
    • Need to write a test for each module, since there is no indirect coverage
    • We’d write a lot of mocks, even for our own code, which could hide actual bugs if the mocks don’t reflect the actual module correctly

Option 2: Focus on integration testing

Whenever a module (a React component, some other functions) depends on other modules that have system interactions we mock the system APIs, like using nock to mock the network requests or mocking react-native-permissions. If a component uses a custom hook that uses react-query to fetch some data, we’d mock the request for “fetch some data”, not the hook or react-query.

Tests without any system interactions won’t need any mocking, for example a pure function can be covered with a neat unit test without mocks.

  • Pros:
    • Increased test coverage of our own code – a single test can cover more than just the module under test
    • Since we only mock system interactions on the “outside”, we can reuse a lot of mocks and don’t need to write them so often. See ‘Bonus option 2’ below for more details.
    • Mocking network requests is more precise than mocking react-query or custom hooks – tests get closer to what the user does, with better runtime than e2etests
  • Cons:
    • Tests can break (false negatives) when imported modules change

Option 3: Combine 1 + 2, label clearly

We use both approaches of mocking outlined as option 1 and 2 above, but clearly label those tests.

// mocks direct dependencies if system interactions are involved
describe('useRecording unit tests', () => {
// mocks only system interactions, not direct dependencies
describe('AssignRecordingGroupModal integration tests', () => {

Within those categories, we apply the rules above, mocking system interactions as close as possible (unit tests) or as far away as possible (integrations), and don’t mix it up.

  • Pros:
    • More flexibility in how we write tests, leaves this decision up to the author
    • A bit more consistency over the status quo, since we at least introduce definitions of unit and integration tests and how they are allowed to deal with system interactions
  • Cons:
    • Need to decide between unit and integration test for each module with system interactions, and potentially defend that decision
    • All the cons of both options

Bonus option 1: Limit jest API usage for mocking

We agree to always use jest.mock() and to forbid usage of jest.spyOn. This is mostly based on various problems we’ve found related to jest.spyOn when trying to switch from babel to swc.

For creating mocks that need to mock a module, but only customize some of it, we try to use jest.createMockFromModule(moduleName)

Bonus option 2: Convention for local vs global mocks

If a mock for a library (module loaded from node_modules) is needed in more than two test files, we move it to __mocks__ (in the root folder). The file name must match the module name used for importing the module, like __mocks__/lodash.js to mock lodash. If named correctly, these mocks are automatically loaded! There’s more details about this in the Jest docs – note the exception about built-in nodejs modules.

Since those mocks are automatically loaded, we try to (incrementally) replace all mocks from jest.config.js‘s setupFilesAfterEnv property and also replace them from setup.jest.js.

Results

Adopt Option 2 (as above) along with the bonus options.

This is the best option to increase test coverage and consistency while keeping the number of mocks low.

We can revisit this decision if it turns out that this focus on integration tests has more or larger drawbacks than estimated here. We could then switch to Option 1 or 3, or refine our approach with Option 2.

Sources