Wednesday, July 30, 2025

Personal View of NYC Congestion Pricing

In the 2010s I managed an engineering organization with teams in California and New York. I travelled to NYC a number of times, typically staying near Chelsea Market.

The Maritime on 16th street was my usual lodging, next to Google's NYC office and with the 14th Ave subway station nearby. I recall the blaring of car horns being ever-present, continuing late into the night.

We brought the whole family to New York City in July, the first time I have been there in almost 10 years. We stayed in Manhattan in the Financial District, and went for pizza very near the Google building. The streets were very clear, nowhere near the level of traffic I remember.

view from high above the streets of Manhattan, with almost no cars visible driving on the roads
(view from the Empire State Building)
ground level view of an empty intersection in New York City
(on the way to pizza)

In January 2025, New York City implemented a congestion pricing mechanism to increase tolls for cars entering the city. It had an almost immediate impact in reducing traffic:

The Federal government, always eager to increase fossil fuel consumption, has revoked the needed authorizations and demanded that NYC end the congestion pricing mechanism. The two parties will present their arguments in court in October 2025.

I hope congestion pricing stays. The city is better for having it in place.

Friday, July 18, 2025

Tello Android settings for VoWifi

Tello is a T/Mobile MVNO in the US which offers good support for Voice-over-Wifi, whereby voice and SMS can be sent using an Internet connection while overseas and not require expensive roaming minutes. We were successfully able to use VoWifi on our recent trip to Europe:

  • SMS text messages arrived
  • SMS messages sent were delivered
  • incoming calls ring the phone
  • outgoing calls work, and carried by usual US number as the callerID

The most important setting we needed to set was "Automatic data switching." My Tello plan includes no roaming minutes at all, I had installed a travel eSIM from Roamless.

Friday, July 11, 2025

Vibe Coding and Wisdom

I started experimenting with Claude Code a while ago. I am not the first person to make this observation, but thinking of Claude Code as an early career developer whom one is mentoring and needs to guide to a solution is a good mental model for it. It is pretty impressive in what it can do.

Asking it to produce something the size of what one would want to see in a code review from an early career developer produces good results, far faster than I could write myself, at a cost of a few cents.

For example:

Add a command line utility written in Go in cmd/adduser. It takes command line arguments for email, phone, imsi, realm, remsim, gpp_hostname, ns, slack_app_token, slack_channel_id, and smtp_list. It encrypts the slack_app_token, slack_channel_id, and smtp_list using the code in internal/db/encryption.go. It opens a connection to the database using the code in internal/db/server.go, and adds a new row to the accounts table using the command line arguments it was given.

This resulted in a quite functional command line program which did what I asked.

func main() {
        var (
                email          = flag.String("email", "", "Email address (required)")
                phone          = flag.String("phone", "", "Phone number (required)")
                imsi           = flag.String("imsi", "", "IMSI (required)")
                realm          = flag.String("realm", "", "Realm (required)")
                remsim         = flag.String("remsim", "", "Remsim (required)")
                gppHostname    = flag.String("gpp_hostname", "", "GPP hostname (required)")
                ns             = flag.String("ns", "", "NS (required)")
                slackAppToken  = flag.String("slack_app_token", "", "Slack app token")
                slackChannelID = flag.String("slack_channel_id", "", "Slack channel ID")
                smtpList       = flag.String("smtp_list", "", "SMTP list")
        )
        //...omit the rest but it was straightforward code...

 

Unit Testing

Claude Code doesn't seem to produce unit tests as a regular part of its development... like some engineers I've worked with I suppose. However it can be prompted to do so and produces a reasonable result.

In cmd/forwarding create a unit test which starts a server on localhost and uses an http client to connect and POST JSON with input arguments.
 
In cmd/user-management create a unit test which starts a server on localhost and uses an http client to fetch /api/account/<uuid>/db

Claude Code produced a quite reasonable table-driven Go test, including creating an in-memory sqlite3 to mock the Postgres database.

+       testCases := []struct {
+               name           string
+               uuid           string
+               requestBody    ForwardRequest
+               expectedStatus int
+               setupDB        func(*sql.DB, string)
+       }{
+               {
+                       name: "successful forward",
+                       uuid: testUUID,
+                       requestBody: ForwardRequest{
+                               Sender:   "test@example.com",
+                               Received: "2023-12-01T10:00:00Z",
+                               Content:  "test message",
+                       },
+                       expectedStatus: http.StatusOK,
+                       setupDB: func(database *sql.DB, uuid string) {
+                               slackToken, _ := db.Encrypt("test-slack-token")
+                               slackChannel, _ := db.Encrypt("test-channel")
+                               smtpList, _ := db.Encrypt("test@smtp.com")
+
+                               query := `INSERT INTO accounts (id, account_number, email, phone_number,
+                                         imsi, realm, remsim, gpp_hostname, ns, slack_app_token,
+                                         slack_channel_id, smtp_list)
+                                                 VALUES (?, 1001, 'test@example.com', '+1234567890',
+                                                         '123456789012345', 'test.realm', 'test-remsim',
+                                                         'test-gpp', 'test-ns', ?, ?, ?)`
+                               database.Exec(query, uuid, slackToken, slackChannel, smtpList)
+                       },
+               },

Unwise Approaches

Attempting to get Claude Code to generate larger systems with less guidance didn't go as well. What it produced was functional and quite impressive, but unwise.

I wanted to try out an admin panel with a login link sent to an email address. Not suitable for all environments, but sufficient for many services which rely on the user's email.

In the admin sub-directory, generate a web user interface for an admin console using typescript and react, with a backend server written in Go.
 
The login screen has a text box to enter an email address. When the Submit button is pressed, the backend server should generate a 128 bit random string and use os.exec to run an email.sh process. The backend server should redirect the user to an interstitial page which says "Please click the login link sent to <email address>."
 
Once logged in, the main page has ...


Claude Code generated a quite functional admin console. One could submit an email address and it would fork the script to send email. It maintained a map of pending login tokens in the Go backend. When one clicked the link in the email the backend would respond with ok it it found that token in its active table, otherwise failure. Quite exhilerating to see all of that work within a couple minutes of starting on it.

However this means the client code, itself, was deciding the success or failure of the login link. If it got an ok from the backend, it would proceed to the URL for the admin panel. The backend code would serve up whatever it was asked for, there was no enforcement in the backend.

Anyone capable of understanding the client JavaScript could figure out the URL of the admin panel for any user. The login link only provided the illusion of protection. It was trivial to bypass.

One can observe that Claude Code generated exactly what I told it to, which is a fair observation. One might also observe that Claude Code just regurgitates its training set, meaning that human developers have done similar things in large numbers. This is also a fair observation.

Nonetheless it reinforces that vibe coding is best used as a multiplier, not a substitute, for actual expertise.

Friday, July 4, 2025

Your Parent Did Not Give Up German Citizenship at 18

map of Germany

There have been a large number of US troops stationed in Germany for decades, since the end of World War II. As happens in these circumstances, a fair number of US servicepeople have started families with their spouse who moved with them from the United States, with children born while stationed in Germany.

Some things which are commonly believed amongst US military families who have been stationed in Germany:

  • Children born to US servicepeople on German soil will be dual citizens of the US and Germany.
  • At the age of 18 or 21 or 23, those dual citizens will have to choose which citizenship they will keep and forfeit the other.

Unfortunately neither of these is true. German citizenship is not like the US: being born on German soil does not make one a German citizen. One is German if one's parent is German, or if one naturalizes. So a child born to two US citizens stationed in Germany is not German. If the US serviceperson marries a German, then any children could be dual citizens.


 

Certificate of Citizenship

This story is reinforced because children born in Germany will have either a German birth certificate called a Geburtsurkunde or, less often, they will have paperwork from the US military hospital where they were born. Neither of these are acceptable as proof of US citizenship, which the child needs when they return to the US.

It is quite common for parents to order a Certificate of Citizenship for their children, documenting that the child is a US citizen. This often happens at age 18 when the child registers to vote or finds a job which requires that they prove their right to work. The Certificate of Citizenship contains language forswearing other allegiences, and reinforces the belief that the child had to choose one citizenship or the other at age 18.

In reality the presence of that language on the Certificate of Citizenship has no impact, other countries do not recognize the US document as being binding upon their practices of citizenship. If one actually was born a dual German and US citizen, the issuance of a US Certificate of Citizenship has no impact on their German citizenship. They remain a German citizen.


 

Impact

The impact of these misconceptions works in both directions:

  1. People who mistakenly believe they are German citizens, or were German citizens, and try to get that citizenship back.
  2. Perhaps more tragically, people with a German parent who believe they forfeited their German citizenship at 18 or 21 or 23 and never pursued it further, when in reality they remained citizens throughout their lives. They could have made different choices had they known.

If you wonder whether you are in this situation, Reddit's /r/GermanCitizenship can help you figure it out. I spend time on that subreddit as well, helping people understand the declaration processes which we navigated.