Saturday, June 29, 2024

Is it 2024 yet?

Whoa, long time no post on my funny little half-forgotten "OG" (as the kids say) Bloggger blog!

Not that I have much to say, I just wanted to check in, update the TLS settings, that sort of thing.

And yet, one ought to write something, oughtn't one?  Mercifully, I shall be brief.  And illustrated!

Here I sit in the middle of 2024, a year of many challenges and also of many adventures, intellectual and otherwise.  I have a view of the river; it's 35 degrees centigrade; rain is likely.  The walls of my home office are covered in art, mostly; but there is also a giant map, and a Buddha of Significant Size, both property of the landlord.

It's tempting sometimes to wax nostalgic about the Old Web, let's say the Internet of the 1990's.  I was there, with the others, building it, and dreaming big dreams of a more open, more democratic world.  Knowledge accessible to all, freak flags flying high, an end to ignorance or at least a noticeable dent in it.  In a nutshell, the opposite happened.  The knowledge part sorta worked, but overshot the mark by miles: you can find any fact, and also any falsehood, in less time than it takes to prompt an LLM.

Also fun to think of the pioneers, people I thought of as my peers back then, in a way probably no engineer ever thought of Mark Zuckerberg as a peer.  Some of the greats, like Andreessen, got filthy rich and just kept on nerding out, a little crypto grift on the side perhaps but basically still enthusiastic dreamers of a better world for all, e/acc techno-optimists.  Long may they live, at least once may they be in power.  But also the little guys: what ever happened to Ev?  I mean, billionaire, sure, but single digits. Oh, right, crappo, after Twitter he started Medium, whose business model is "paywall for Blogspot."  Can't win 'em all.

I've reached the point where some of my friends are probably quiet billionaires.  Well, acquiantances anyway.  I'm quite sure a few are over the 100M mark, I could probably even name them.  Funny, I think in tech you reach that point pretty fast now, but as far as I know none of my peeps was stone-cold rich until they'd been in the game for a couple decades.  Excluding the ones who started off rich, of course.

It's all down to the inside connections and owning those appreciating assets.  And yes, hard work, if you're the builder type, which I am.  An artist of the builder type.  But I've never been good at networking, and I've never been good at hoarding anything other than art.  Is it too late to become a dealer?

Well on that note of strained optimism, I introduce you to one of my favorite little paintings on paper from my days in Budapest.  Title:  "I am a victim of the Science Age."  Helps if you say it in your best DLR voice.  It's on my wall as I write this.  And I think I'll use it as an avatar for a while, it seems to fit.



 


Friday, October 27, 2023

Wise (Transferwise) Cancels US Business Debit Cards with Two Days' Notice

I dunno, this seems like it will get picked up by people with more influence and be the flaming pile of story it deserves to be, but still, in my small way, I wish to add to the Permanent Internet Record.

I use Wise (formerly Transferwise) a lot, it's a low-friction way to deal with money in transit when your life involved multiple currencies. (Mine involves at least five.)  The fees are... well the fees are a lot better than what your bank will charge you for the same service, and that's the Wise business model.

A while ago I got a business account from them, for an actual business I started in California.  A bootstrapped tech thing.  I was hoping for more of that well-programmed low-friction goodness -- and except for a colossal double-charge fuckup that ended up costing me about $100 in fees despite it being 100% Wise's mistake, a mistake I even documented for them in a nice "steps to reproduce" fashion: except for that, it was really good.  And the fact that I ate those fees tells you how much I liked it overall.  I figured I'd saved a lot more than that by using their service, so I wasn't going to bother fighting them on what was clearly a software bug, I would just be "wise" about how I used my debit card in the future.

(Detail: they approve charges based on prior approvals for the same merchant, even if you don't approve the new charge; and if you pay from a basket of currencies and then get the chargeback in a different basket, all conversion fees are on you.  Fun bug to fix, I hope someone actually did.)

Alright, so fast-forward to today -- and suddenly, with two working day's notice they are cancelling all US business debit cards.  Lovely.

Not that big a deal for me: an inconvenience, surely, but having my November payments bounce is not going to tank the company, I'm still bootstrapping and there's not that much going through.  But imagine you're further along with your startup, and have a big AWS bill to pay Or Else.  Well in that case, you've just been screwed by Kristo Käärmann.

This looks to be a massive screw-up, but of course I have no idea how massive.  Maybe there just aren't that many of us using Wise for US businesses with legit expenses?  (The accounts still work, so if you're using Wise for, say, sanctions-busting payments to your Russian dev team, that's probably still on.)

My best guess, after their software bug bit me in the double-charged ass, is that they were caught off guard by some intricacy of the US system and are as disappointed as their customers, but without the actual pain.

But they didn't have to add insult to injury, did they?  As of this writing, the "more info" link in the email takes you to a page with... wait for it... the exact text of the email itself, verbatim.  Also, "temporary" doesn't mean what they think it means, but I will cut them non-native-speaker slack on that one.

So there you have it.  Hello effing World.  I need to find a new provider for my small-business account, and so does every other US business that's used Wise.  (I wouldn't trust them with a business account in any country now, but to be fair, I'm not going to stop using them for moving currencies around.)

I have to admit I'm now very curious how big a deal this ends up being.  My heart goes out to those who trusted Wise with their business banking, as I was certainly planning to do.


Saturday, August 05, 2023

Background Items Added

Many of us, suffering under Apple's declining software quality, have encountered the Background Items Added plague.

After much annoyance -- Spotlight, which itself has gotten worse in recent releases, does not find the culprit files despite their containing the search string in the filenames -- I believe I have solved this problem.  YMMV, works for me, etc.

First, go to Settings and set the Background Items the way you want.  This may require some digging since the new Settings app is a UX faceplant, but hey, you'll get there.

Next manually delete, from your Applications area, the offending apps if you can.

Then do a reboot just to be sure.

Now, when you see alerts like the one shown below, go check in two directories:

~/Library/LaunchAgents

Here you may find gems like com.spotify.webhelper.plist and you can simply remove them.

/Library/LaunchAgents

Here you may find things like com.canon.usa.EWCPairingService.plist  and com.canon.usa.EWCService.plist -- these require admin removal, meaning "sudo rm" if you're in the Terminal or "use password" if in Finder.

I personally recommend zapping these in the terminal, because I don't fully trust Apple to not give them some weird zombie powers to exercise from the trash.

That's all for today.  Just wanted to put this out there in the Internets.  Fucking Apple, man, really.



Sunday, May 14, 2023

ChatGPT on Democracy

Thailand votes today; and in the West many things are afoot. Here's hoping the AGI is not an authoritarian populist!

Advantages of Democracy:


1. Protection of Individual Rights: Democracy emphasizes the protection of individual rights and freedoms. It provides a framework where citizens have the right to express their opinions, participate in decision-making, and hold their government accountable. Democracy promotes equality before the law and protects civil liberties such as freedom of speech, assembly, and religion. This ensures that citizens have the opportunity to live a life of dignity and have a say in shaping the policies that affect them.

2. Rule of Law and Stability: Democracy is typically built on the principle of the rule of law, where laws and regulations are established and enforced fairly and impartially. This ensures stability and predictability within a society, fostering an environment conducive to economic growth, social progress, and peaceful coexistence. In a democratic system, institutions are responsible for upholding the rule of law, and leaders are subject to legal checks and balances, reducing the risk of arbitrary exercise of power.

3. Peaceful Conflict Resolution: Democracy provides a mechanism for peaceful conflict resolution and political change. Through free and fair elections, citizens can choose their representatives and hold them accountable for their actions. This peaceful transfer of power helps to prevent violent upheavals and civil unrest that can arise in authoritarian regimes. Democracy encourages dialogue, compromise, and negotiation as methods for resolving differences and addressing societal challenges, promoting stability and harmony within a diverse society.

Threats to Democracy:


1. Erosion of Democratic Institutions: One of the significant threats to democracy is the erosion of democratic institutions and processes. This can occur through the concentration of power in the hands of a few, weakening checks and balances, undermining the independence of the judiciary, or manipulating electoral systems. When democratic institutions are weakened or co-opted, it can lead to authoritarian tendencies and a decline in democratic governance.

2. Populism and Polarization: Populist movements and growing polarization pose threats to democratic nations. Populist leaders often exploit public discontent and divisions, fueling resentment and promoting an "us vs. them" mentality. This can undermine democratic values, weaken social cohesion, and hinder constructive dialogue and compromise. Polarization can lead to a breakdown of democratic norms and the erosion of trust in democratic processes.

3. Disinformation and Manipulation: The rise of digital technology and social media has facilitated the spread of disinformation and manipulation, posing challenges to democratic societies. False information, propaganda, and targeted campaigns can influence public opinion, undermine trust in democratic institutions, and manipulate electoral outcomes. The rapid dissemination of information through social media platforms without proper verification mechanisms can contribute to the creation of echo chambers and the erosion of shared facts, making it difficult to have informed public discourse.

It's important to note that these advantages and threats can manifest differently in different democratic nations, as each country has its unique political, social, and cultural context.

Tuesday, December 13, 2022

ChatGPT Does Nihilism

A Poem About The Sun


The radiant sun so brightly shines
But its warmth brings only pain
A beacon of light in the sky malign
A source of death and sorrow again

The sun is a fiery sphere of death
A brilliant, burning orb in the sky
It scorches the earth with its fiery breath
And brings destruction to all that passes by

In endless cycles it does rise and fall
A never-ending dance in the sky
A symbol of power and terror to all
A guiding light, a reason to die

Its rays stretch out across the land
Illuminating all that they touch
A symbol of death, destruction, and command
A source of agony and despair to all that it cushions

We rise each day and face the dawn
With fear and dread to guide our way
And in the evening, when the day is gone
We curse the sun for another day.

We cower in its warmth and glow
And are terrified of its presence
A shining star that we all know
The sun, a harbinger of our demise.

-- ChatGPT, 14 December 2022.

Thursday, December 08, 2022

StableDiffusion Does Politics

 Just for a laugh, I had StableDiffusion render two of the American right-wing's enduring obsessions.  

Behold!

Hunter Biden's Laptop and Hillary Clinton's Emails!



I do love how the AI thinks of human hands.  And just so nobody thinks I'm neglecting my whataboutisms, here is Donald Trump in Florida.  Have a nice day!



Friday, December 28, 2018

RyanAir's Non-Flexi Flexi Gotcha

Having taken a few RyanAir flights in the past week, I found myself tired of the utter chaos that is RyanAir check-in anywhere in Spain, and decided to book an Iberia flight out of Madrid instead of using my RyanAir ticket. Since I'd booked a Flexi Plus ticket and paid quite a bit for it, I thought I'd try rebooking.  It occurred to me that might not be possible as I'd already checked in, and of course I'd checked in because RyanAir strongly encourages you to do so far in advance of your flight.

Despite this being exactly the kind of rat-bastard thing a cheapo carrier might do, I thought I'd give it a try. And I wasted an unrecoverable hour of my life following the instructions on the website, which clearly stated that I could rebook even after checking in, and that I'd merely have to check in to the new flight immediately.But for some reason it wouldn't let me go to the final booking stage.  I could find a flight, select a fare, and.... nothing.

As a Computer Guy, I thought maybe they just didn't work with Safari, so I tried Firefox.  This sometimes works with large brands, which is in itself scary.  No luck.  I checked for Javascript errors in Safari and found a lot, plus a great number of 404 Not Found responses for their own Javascript resouces. At this point I knew I was dealing with seriously buggy software, so I tried the Customer Support Chat.

That, it turns out, in another typical rat-bastard move, is not a chat at all but rather a sub-Eliza-level dichotomous tree of automated FAQ responses peppered with "please use fewer words" responses.  And finally I got theanswer.

If you checked in early, as the Ryanites urge you to do, then you must contact customer support (through what means I cannot fathom -- probably by phone) and get yourself checked back out -- subject to office hours -- at least one day prior to departure, otherwise you can not do what the website says you can do and lets you spend your time halfway doing: you can not change your flight.

So my original instinct was correct: Rat Bastard RyanAir One, Customer Experience Zero.  Plus I shudder to think how awful it must be to write their broken software, presumably in some low-wage sweatshop with a sweaty-faced remote manager screaming at you in Irish from an unheated basement in, I kid you not, the town of Swords.

Sunday, December 02, 2018

George Herbert Walker Bush

I am sometimes reminded that for many of us, George Herbert Walker Bush was, during his Presidency, a symbol of the cynicism and brutality of a dawning corporatist world order. And I cringe: how good we had it, to have that guy as our bad guy. His “kinder, gentler nation” was not to be. His rationalist statesmanship was the last of its kind in the White House. His world order was patrician, but compassionate. And he walked the walk, even if he wasn’t so good at talking the talk thing. I did not support him politically, and I do not do so retroactively — but I know he would have respected that choice and, had the occasion arisen, been capable of a serious conversion about that. Rome wasn’t built in a day, and who knows how long it’ll last, but it was built by people like 41.

Friday, March 02, 2018

First Class May Not Like Me

Spoiler alert: white people problems!

I sometimes fly business class. This is a luxury, sometimes a decadence, but it's one of the few I allow myself and I shop around for reasonable prices. Over the last few years British Airways has become my go-to airline for business flights between Europe and San Francisco, a trip I make at least twice a year. And I hate flying, and it softens the blow. Woe is me, etc.

For the current trip there was a relatively good price on First Class tickets. "Whoa," thought I, "First must certainly be better than Business, and I've never tried it, so..." I was, frankly, more interested in checking out the First Lounge on my six-hour layover. I wondered what it might have in store.

TL;DR: I still wonder.

Flurries in Albion

I packed, I futzed around, and I slept my customary three hours before an early morning flight. I got a cab, I went out to Budapest's Ferihegy Airport, which the right-wing nationalists have renamed after an epically philandering musician with excellent hair. Everyone still calls it Ferihegy.

And upon checking in -- for flights to the US one does not check in online -- I was informed that the flight was three hours late. "Mechanical?" (As if I had the conviction to not fly when there are mechanical failures.) No, the plane arrived late the night before, and the law mandates a reasonable rest period for the crew. Which I wholly support, though it seems within the realm of the technically possible for a Flag Carrier to send a text message when they know twelve hours in advance that the flight will be three hours late. I talked with other passengers: nobody was informed. Not even an e-mail.

I understood what was really up when we got to London. But first the waiting, which I hope is less boring in the telling.

Budapest Ferihegy has two "business" lounges: one in the Schengen area, i.e. flights to the rest of Europe, and one past the passport control for those bound for non-European destinations such as Delaware, Bamoko, or London. The former lounge should be avoided at all costs unless you absolutely, positively can not pay for a drink, even on credit, at one of the numerous kiosks. The latter is acceptable as long as you're not hungry and you have a good understanding how the global services octopoid Celebi manages expectations.

Having bought my Economist (cash) and my first drink and sandwich (apology voucher) in the normal Schengen area, I got my passport stamped with a B for Brexit and headed over to said Celebi lounge. Some bubbly, cookies with jam, a nap, and the useful knowledge that the other passengers had not been informed of the nature of the delay. I felt a miniscule sense of superiority, surrounded by my economic betters, in knowing exactly why we were all swilling the "Hex" bubbly and dozing our best two-minute dozes. After all, had my ex-in- laws not believed I was a spy?

We boarded exactly three hours behind schedule and proceeded to wait another twenty minutes for a runway slot. Celebi, expectations, see also. The flight itself was uneventful and the crew quite pleasant, which was unsurprising considering the rest they'd gotten. When we got to London Heathrow we had to wait another 15 minutes or so before landing. At this point the people with tight but feasible connections started getting nervous.

Landing was uneventful, and there was the lightest dusting of snow on the edges of the runway, as if the Fairy of Winter had stopped by for a coffee but only had time for a ristretto and a kiss on the cheek before heading off to Finland.

This, it turns out, counts as a severe weather event in London. Apparently Old Blighty can absorb an unlimited quantity of rain, but a few millimeters of snow grind it to a screeching, if mostly polite, halt.

The aircraft reached its standing position. The jet bridge failed to budge. Within a few minutes our Captain engaged the microphone to inform us that the jet bridge was broken and we would use the stairs, as soon as a set of stairs could be obtained, which surely would be within minutes.

Minutes passed.

After another ten or so we were given to know that we had a staircase and needed only to attach it to the aircraft and we could be on our way. Those who had not already rebooked their onward flights, and those who had rebooked to short connections, breathed a sigh of optmistic relief.

For another ten minutes passed, during which our fearless Captain went so far as to open his window (who knew this was possible?) and shout at the ground crew. We were again informed: apparently another plane needed the stairs first. Heathrow is a busy place. We're next in line.

Some minutes after that the story shifted slightly: we need two staircases, presumably one fore and one aft; the second has to be brought from Terminal 5. Having made this connection a half dozen times before, I knew that the mandatory bus ride to Terminal 5 takes about ten minutes without a rolling staircase in tow. I started to worry for my own connection.

Another ten minutes and we had our stairs: one case of them, attached at the aft. Everyone out into the bus!

The bus was completely normal, and when the ground crew said it was full and could only take two more people -- of whom I was one -- I felt a guilty gratitude. When I boarded the bus I saw it could easily take another 10-15 people, and in the end that's what it did. We all fit, and entered the intentional labyrinth of Heathrow, having sacrificed a four and a half hours to the incomprehensible phenomenon of snow -- Snow! -- in Albion.

Holy Grail Lounge

Having made it into the maze, a maze I fortunately knew from previous trips, it was really down to time. One needs an hour, minimum, to get from arrival at Terminal 3 to the departure gate at Terminal 5. I had 45 minutes and a burning desire to see the First Lounge.

At this point I realized that seeing the First Lounge would require that I encounter almost no resistance at the various waypoints: the many walkways and stairs and escalators, the trans-terminal bus, the security check, and the mini-rail within Terminal 5. In the best case, with a little hustle, I figured I might have 15 minutes clear in the lounge, enough to look around and use the private washroom and throw back a Gordon's & Fever Tree. My plan was to make it with at least 10 minutes clear and overstay the printed boarding time by another 10. Hey, First, they'll wait for a bit.

Things worked out differently, but in the end it wasn't so bad.

First, I made all the connections with minimum friction. There was a long line for the Terminal 5 bus, and the two busses available filled fast, but despite a little chaos I was on the second. (I'm sure there were those behind me who missed connections, thank you LHR.)

Second, having been very diligent, I knew from the info-screens and also from Big Brother Google that the flight was on time and I should go to the general vicinity of the C Gates. This I did.

Having arrived with about 10 minutes clear, I started looking for the lounges. No luck. A helpful gentleman selling apothecary goods told me there were none, I'd have to go to the B Gates. Whence I had come, more or less, on the little tram.

I tried that but failed to find the way, owing to difficult signage combined with fatigue. I ended up, frustrated, back at the C Gates, looking around in vain. Finally I noticed the flight was delayed by 30 minutes, a new development since I'd gone looking for the tram. Sir Apothecary told me in detail what I'd done wrong, and off I went on foot (no tram from C to B!) to seek the coveted First Lounge. Now I had, I thought, at least 20 minutes clear.

In B-Land I found the lounge... but only the Business Lounge, which I knew well and do still like. I asked for help. I learned:

  1. There is only one First Lounge, seek it not amongst the B's and C's!
  2. BA requires your presence 20 minutes before takeoff, not 60.

The lovely receptionist told me to chill out and come back in 30 minutes to check the flight status.

In 30 minutes in the Business Lounge you have exactly one G&T (Latin Strength) and a relaxed visit to the Washroom, hallowed be thy name.

After which, the lovely receptionist suggested I'd just hit boarding if I left right now. First Lounge remained a Grail.

Leave I did, and I caught the boarding wave just in time to be "randomly" selected for additional security screening. Among 10 "randomly" selected passengers there was exactly one woman, who happens to be sitting next to me in First as I write this. We computer folk would strongly question the cryptographic strength of that rand function.

3... 2... 3... 2...

Now, a little tired, having been travelling since 4am Albion Time, at about 15:00 I boarded. Immediately I was taken by the arm, addressed by name, and shown to a window seat. I had reserved an ailse seat, as the windows were (and generally are) all reserved before they start dropping the prices for ruffians like me.

I mentioned the ailse reservation to my concierge, and he said British Airways apologized for the trouble today. Things started looking up. I settled in. I instagrammed.

And then along came the rightful occupant of the seat, an older and presumably richer California ex-hippy type, who was very kind but hey, it was her seat and First on British is always full. She probably owns Burning Man and 0.2% of Google, and yet she was totally cool about it.

I was unceremoniously relocated back to my viewless accommodation. Though viewless is not quite the word: there is the beautiful securitized neighbor typing away on her beat-up Intel Inside PC; there is the Running Man on the Exit sign (running where?); there is a TV with a hundred channels, and mint tea, and wine until you give up or pass out.

Meanwhile... the snow. Or let's just say the cold.

Departure was 15:30.

Security checks (blame the Americans) added at least 20.

Then various parts needed to be de-iced.

Through all of this I was given top-shelf bubbly, and eventually I dozed off. I awoke at 17:00, yes 5pm, to our accelleration. For a brief delirious moment I was sure we'd not reach escape velocity, but we took off without incident.

Two hours late. Or, if you like, seven hours late.

Since then it's been a pleasant ride. I learned that Plane Pyjamas are a Thing. I learned that in First all the staff remembers your name, just like in the fanciest business hotels. (How do they do that? Could it be monetized?)

And of course I was reminded why I fly British Business even when the lightest snow flurry puts their Queen and her Corgis in a twist. The seats are nice, the flight crew is for the most part awesome, and they are bottomlessly indulgent to those of us who wish to keep the terror of air travel at bay through the magic of a strong and steady buzz.

But still, dude, you couldn't manage an SMS at midnight?

As we cruise over Calgary I look back on the experience. There's no telling which of the early screwups were British Airways' fault, versus Heathrow's, versus London's, versus the Act of God that resulted in a little ice and that light dusting of snow. On the plane, the divider between my sleep pod and my neighbor's didn't work. The in-flight entertainment system didn't work for half the First Class passengers (a faulty cable perhaps). The noise-canceling headphones only canceled noise when plugged into the entertainment system and not when plugged into my phone. They ran out of water bottles, though not potable water, so that may actually be a positive thing.

Is First worth any more money than Business on BA? Without the First Lounge experience I can't really say. I only paid 100 EUR more in the end thanks to BA's weird habit of charging for seat reservations in Business. For that small difference I'd say the larger sleep-pod is probably worth it, but I get the impression First is more malfunction-prone than Business. Or maybe the world was just having one of those days.

Note to Future Self in any case: download some classic binge-watchables on iTunes next time I fly British, and bring my own noise-canceling headphones.

Monday, April 03, 2017

A Thought on Airlines

I recently bought a ticket on AirBerlin, which is my preferred airline for traveling between my home in Berlin and my favorite destination of Budapest.

It was a horrible experience.  Their preferred payment system, the security of which seems questionable, failed to process my payment (from my Berlin bank no less); it was impossible to return to a booking in progress if the browser window closed (hello, cookies?); and in the middle of retrying this, literally within minutes, they raised the price of one of the flights.  And that's just the technical part.  It was impossible to figure out which fare class would actually be cheapest once luggage and seats were added; it wasn't obvious who operates the flight (Alitalia, says SeatGuru, which would be a first for this route); and in general things were designed to keep the customer in the dark.

Unfortunately, there's nothing particularly unique about that.  Airlines are in general hostile to transparency, and constantly try to squeeze you for an extra five Euros here, an extra $19.95 there.  (Here at least the cheapos like WizzAir are honest: pay for the good seat or your trip will suck.  Pay for your booze, but they'll be happy to sell it to you.  Ryan and Easy, however, remain below contempt.)

The strange thing in all this is that I expect my flight to be pleasant, professionally flown, and more or less on time.

And it's like that almost every time.

We buy a ticket from a company that obviously hates its customers, and we then put our lives in that same company's hands.  And somehow this works.


Wednesday, November 09, 2016

Ten Quiet Predictions for a Trump Presidency

It’s done: in an upset that should have surprised no one, Donald Trump has been elected President of the United States. As to how this happened and why, the pundit classes have been so busy dissecting the cause and the method that they didn’t look up to see the oncoming train. The train is upon us now, lumbering forward in its orange sputtering of nonsequitur trutherisms.

For the moment, I’m not very interested in how we got here; as an American from the sticks, I accept that he speaks for the majority. Most of that majority really does stand behind him, and a few (like Spineless Paul Ryan, and most of the Religious Right) are mere opportunists who have nonetheless ceded their voice to the Orange King. He is their voice. For real this time.

(I read somewhere that the Republican Party had created the monster that is Trump’s base; and Trump had stolen their monster for his own uses. This is probably true, but it’s also true that this kind of talk inspired the monster to don a Deplorable Me t-shirt and get out to vote.)

It’s early. Hillary has conceded, so there is no second-guessing Trump’s victory. (And in at least one point I’m relieved: I will not have to see a second attempt at dynastic leadership just yet, at least not until Ivanka runs).

The Republican Party – the Trump Party, as it frankly ought to be renamed, its logo painted in gold, with a 5% members’ discount at select properties – will also control both houses of Congress, and most likely the Supreme Court with the ideologue of their choice by mid-January.

That means they will have to govern, or at least try. This will not be easy, especially after so long in cushy obstructionism. The likes of Ryan are a little too used to writing budgets without using math, and taking responsibility will be hard. But I do believe they will take action, because without action the Donald will bore and worry and fear for his popularity; and if there’s one thing we now know for certain, it’s that the Orange King can eat Ryan and his covey of twerps for lunch.

And now to my predictions. Like most predictions, they will most likely not age well. But here we go all the same.

1. Trump will return to a limited pragmatism.

He will certainly remain an oafish, bigoted conspiracy theorist, but as far as he can he will avoid outright confrontation with half the country.

Instead he will try for things he can win without protest, or win despite protest, or look good losing.

Ivanka will be chief of staff and unofficial COO.

2. Environmental protections will be gutted to the point of meaninglessness.

Fracking ho! This will be the price of the Koch brothers’ support, but Trump will pay it gladly as time and again his base has come out in self-defeating opposition to government regulation of industry.

In the short term, the poor will pay the price as they usually do. Long-term this means the worst for climate change, but the worst was coming anyway.

3. Black Lives will Matter Less.

This one is obvious: after all we’re talking about the Birther President.

The government will no longer support any efforts towards racial equality. The Justice Department will no longer pursue vote-suppression investigations. There will be laws against recording police actions on video.

These things will be to the great satisfaction of the white supremacists, but Trump will appoint a few token People of Color in his adminstration and swear he’s not a racist.

4. Assad will win in Syria, with Russia’s help, and with some partition.

Trump will wash his hands of Syria, but look like a statesman of sorts for brokering a deal between Turkey and Russia that will give most of Syria back to Assad while carving out a small zone for the anti-Assad forces loyal to Turkey. The Kurds may or may not be sold out – largely depending on whether Trump learns anything about them before making the deal.

The deal may well be proposed by Russia, but giving Trump credit will be part of the deal.

5. Trump will play chicken with Mexico and Canada on trade, and win.

I think NAFTA will be an easy target for Trump. It’s insanely unpopular with his base, who blame it above all else for the gutting of American factory jobs. And while it certainly benefits the US as such, that benefit accrues mostly to big business.

His argument will be that NAFTA was a lousy deal for the USA, and that Mexico and to a lesser extent Canada are the big winners. He will demand concessions, maybe even a whole new treaty. And he will get his concessions, particularly from Mexico.

The big question is whether they will be symbolic concessions or substantive ones. If they are substantive, we might see it helping agriculture in the US, but I have a hard time picturing it doing anything for manufacturing.

6. Trump will play chicken with China on trade, and lose.

Bolstered by the popularity of his NAFTA re-do, Trump will try to “get a better deal” with China. China will play him like a Guzheng and extract serious political concessions in return for meaningless trade platitudes, then use the cudgel of their dollar reserves to prevent anything substantive changing to their disadvantage.

Trump will find a way to spin this in his favor, but the financial press will give him a very hard time for it.

7. US isolationism will be taken advantage of across the globe.

China, Russia, Iran, Pakistan, Saudi Arabia, North Korea, and Poland – just to name a few – will all do things at home and with (or to) their close neighbors that would previously have gotten them in deep trouble with America. Trump will let it slide.

His political intuition on this point will turn out to be right: most Americans will prefer to not care who’s killing whom on the other side of the world.

8. Obamacare will be abolished, but the pre-existing-condition protection will remain.

This point, and the next, are where I see a glimmer of optimisim around the Trump presidency. I think he has enough populist sense to not take away something as important as the pre-existing condition protection. (For any international readers: pre-Obamacare you could not buy individual insurance in America that covered any illness or injury you already had.)

I also think Ivanka will push him to do this, and unlike any pre-Trumpian Republican he will not have any problem sticking it to the (obscenely profitable) insurance industry, nor will Spineless Paul Ryan be able to stand up to him. On anything, really.

9. The tax code will be reformed.

Over the objections of the business elite, and to the consternation of the entrenched Republicans in Congress, Trump will force through a simplified tax code that even he can explain to the people. It won’t be great for anyone but the rich but it will “seem fair” at first glance.

As with the “something better” for Obamacare he will face stiff opposition from his own ranks, but he will bulldoze them, and also get some unexpected support from left-leaning Democrats if the tax is not completely regressive.

This, together with the NAFTA deal, will be his signature achievement.

If he gets the Trump Party in line early, don’t rule out a flat tax on income, possibly with a lower rate for investment income. This kind of regressive tax plays well with the middle class because it “seems fair,” and it also buys a lot of loyalty from the upper-middle class that might not be with you ideologically. It worked in Hungary and I’m sure Orbán will suggest it to Trump at some point.

10. The poor will get poorer, etc; victory will be declared.

Chaos, unpredicatbility, and amateurish mistakes will exacerbate the problems you’d already expect from the normal, growth-killing Republican policies.

The economy won’t tank unless there is a major unforeseen catastrophe, but it will be sluggish at best. That part of Trump’s base that could be called the economic losers of globalization will be even worse off than they are now, and except for the very rich nobody will be much better off.

But Trump will have a few big wins to offset his big losses, and the Democratic Party will be split as usual between the business-establishment wing and the Sanders leftists.

2020 is his to win, looking at it from here. But to do it he has to be a little bit lucky with global events – no new wars, no economic meltdown – and he has to avoid the temptation to stock his administration with characters from the clown car of his recent campaign.

While I’m sure there will be pressure to appoint, say, Rudy Giuliani as Attorney General, I hope that Trump the Opportunist at least recognizes that he’s now the Winner and can choose from a more competent class of sycophants.

Thursday, April 14, 2016

Interesting JSON Benchmarks Go.

These days lots of people are buiding microservices, and microservices usually involve HTTP API’s, which in turn usually exchange data as JSON.

Not long ago somebody pointed out that a lot of effort goes into generating and parsing that JSON. It would be unwise to simply ignore this part of your system’s design.

Since it’s very easy to benchmark things in Go, I decided to do a quick comparison of JSON encoding strategies.

Go JSON Encoding

The normal way to generate JSON in Go is to use the encoding/json package, and feed your struct into the MarshalJSON function. This function will take anything and try to convert it to JSON. If your struct, or anything in it, has its own MarshalJSON function then that is used, otherwise it’s examined using reflection.

Reflection is (supposed to be) expensive, so I wanted to see how much I might save by making my own JSON encoder for a struct. The main point being that I already know what the struct is made of, so I can save the encoder the trouble of examining it.

Benchmarked Variations

I started with several, er, structurally identical structs:

  1. A naïve one, with no MarshalJSON function of its own.
  2. A hinted one, with field names provided.
  3. A smart one, with its own proper MarshalJSON function.
  4. A fake one, which returns previously set data from its MarshalJSON.

The point of the fake one, of course, is to isolate the overhead of the actual JSON encoding.

All of these, when set up with a bit of standard fake data, generate the following JSON:

{
   "Id" : 123,
   "Stuff" : [
      "fee",
      "fi",
      "fo",
      "fum"
   ],
   "Desc" : "Something with \"quotes\" to untangle.",
   "Time" : "1970-01-01T01:16:40+01:00",
   "Insiders" : {
      "One" : {
         "Id" : 321,
         "Name" : "Eenie"
      },
      "Two" : {
         "Id" : 421,
         "Name" : "Meenie"
      }
   }
}

Surprising Results

Here are the benchmark results for this little experiment, as run on a MacBook Pro (Mid 2014) with 2.8 GHz i5, 8 GB RAM, under Go 1.6.1.

BenchmarkNaïveJsonMarshal-4               300000          4299 ns/op
BenchmarkHintedJsonMarshal-4              300000          4293 ns/op
BenchmarkSmartJsonMarshal-4               200000          6490 ns/op
BenchmarkSmartJsonMarshalDirect-4         300000          4149 ns/op
BenchmarkFakeSmartJsonMarshal-4          1000000          2299 ns/op
BenchmarkFakeSmartJsonMarshalDirect-4   10000000           115 ns/op

In the “Direct” benchmarks, the struct’s own MarshalJSON function is called without going through encoding/json, i.e. without any sanity-checking.

I expected to see a lot of overhead from the reflection, i.e. the unknown struct being examined. Instead I found that using your own MarshalJSON function is actually slower because json.MarshalJSON (sensibly enough) validates the JSON output for you, lest it accidentally return invalid JSON itself.

Also, the hinting doesn’t make much of a difference, but it can make your JSON output prettier and more predictable: one usually uses it to have lowercase and/or underscore_separated key names in JSON objects, and to omit null objects in order to compact the JSON.

Using the numbers above we can very crudely estimate:

  • Custom encoding with validity checks is about 50% slower.
  • Custom encoding without validity checks is about 3.5% faster.
  • Best-case custom encoding with validity checks is about 50% faster.

In order to use the custom encoding without validity checks, you have to do all the encoding in a non-idiomatic way. This makes your codebase more fragile, because a new collaborator can’t just step in and do the obvious thing without undoing your optimizations.

It would be interesting to see how these numbers scaled with more complex structs, in particular deeper nested objects.

Based on these benchmarks, which I admit are oversimplified, I recommend avoiding custom MarshalJSON functions unless you absolutely need them for handling unusual data structures. If you want them for speed, make sure to benchmark your implementation before making a final decision.

Source Code

Wednesday, April 06, 2016

I Am Possibly Legend

Recently I finally – finally! – got around to seeing the 2007 Will Smith SciFi vehicle I Am Legend. Apparently it was a remake, of sorts, of Omega Man, which I saw as a teenager and barely remember.

Spoiler Alert! Just in case you haven’t seen it.

Here’s the really big problem. The Zombies aren’t trying to kill Will Smith because he’s the savior of the human race, they’re trying to kill him because he’s a serial killer, responsible for hundreds of disappearances, tortures, and grissly murders. He’s more than a bad guy, he’s Zombietown’s own Josef Mengele.

He abducts the Zombie King’s girlfriend/daughter/wife/buddy/chess-partner (we aren’t told which), drawing the Zombie King out of his lair at risk of death by exposure. Our Hero then performs sick medical experiments on her, fully expecting her to die of them, in the name of “curing” her. She dies.

Then, the Zombie King – proving, by the way, they’re not zombies at all in the traditional film sense – comes up with an elaborate trap to capture Lt. Col. Mengele. After that fails, the Zombie King directs an army that almost kills Our Hero, but the Lt. Col. is saved by another NonZombie who shows up out of nowhere.

It should be said that Our Hero is at that point basically suicidal, because the Zombie King’s dogs zombified the Hero’s dog in their failed capture attempt, and the dog was Our Hero’s best and only friend, there being no other NonZombies around, and to top it off Hero had to kill Dog to prevent the zombification.

But! But all along our Hero makes audio notes, such as the audience is privy to, in which he says things about the Zombies that are demonstrably not true. Mostly, that they are actually zombies, and not merely Very Different Humans.

The only part of this I don’t understand is whether the filmmakers were trying to gloss over the murderous aspect, or whether they were through it trying to point out the futility of all life, of our folly before the gods. Because – spoiler alert – in the end the Good Dr. Lt. Col. Hero’s life’s work of abduction, torture and murder succeeds, and ends the plague of zombism.

Well, if you believe the narrator it does. Considering how reliable the narration is up to that point, I am much more inclined to believe there was a successful genocide launched against the Zombies. And for that matter I am specifically disinclined to believe they ate all the nice folks in the first place.

Saturday, March 12, 2016

Many Servers in One, with Go.

I’m still fiddling with a web server idea, currently trying my umpteenth version of it in Go. One of the things I want to do is serve a bunch of different things from one binary; they would usually live behind Nginx anyway, and I like the simplicity of serving a bunch of (somewhat) dynamic, small sites from a single program.

So, how does one do it? Why, one does it with Goroutines!

I had expected that to be the answer, but even then I was surprised at the simplicity of this solution. You don’t even need channels, since http.ListenAndServe is a blocking call if it’s not in a Goroutine.

Here’s the code, which should be self-explanatory.

Friday, January 08, 2016

How to Test Logging in Go

Google’s Go language has a very rich and at times utterly infuriating standard library. One of the things it includes is Logging. Since there are six million ways to log (choose one!) we shouldn’t get too worked up about the ways in which the log package doesn’t log the right way for, well probably for anyone outside of Google. That’s not the point.

The point, and it’s a happy one today, is that a mere ounce of prevention will get you easily testable logging with Go’s standard log package. Caveat lector: I have no idea whether this works with Go prior to version 1.5.

Your Ounce of Prevention

Especially when hacking together quick utilities or packages, it’s always tempting to do this:

package logdemo

import "log"

func Log() {
    log.Println("woo hoo")
}

However, that will be very hard to test. Take the time to set up a logger, and expose it. Ideally you can do this with a struct:

package logdemo

import "log"

type LoggingThing struct {
    Logger *log.Logger
}
func New(name string) *LoggingThing {
    prefix := "thing-" + name + " "
    flags := log.Ldate | log.Ltime | log.Lmicroseconds
    return &LoggingThing{Logger: log.New(os.Stdout, prefix, flags)}
}
func (lt *LoggingThing) Log(msg string) {
    lt.Logger.Print(msg)
}

Now you have something that can be messed with in your unit tests, and is also much easier to debug should the need arise.

But if you really can’t deal in structs, at least set up a package variable for the logger. This too can be manipulated.

package logdemo

import "log"

var logger_prefix = "global "
var logger_flags = log.Ldate | log.Ltime | log.Lmicroseconds
var Logger = log.New(os.Stdout, logger_prefix, logger_flags)

func Log(msg string) {
    Logger.Print(msg)
}

Your Pound Sterling of Cure

Now that we have an exposes Logger (two, really), we only need to know one thing: that a Logger can have its Output set after the fact, and that the Output needs only to be an io.Writer, which as you may recall simply requires that it implement this:

type Writer interface {
        Write(p []byte) (n int, err error)
}

In our test package, we can set up a simple struct to catch logs instead of writing them:

type LogCatcher struct {
    Logs []string
    Last string
}

func (lc *LogCatcher) Write(p []byte) (n int, err error) {
    s := string(p)
    lc.Logs = append(lc.Logs, s)
    lc.Last = s
    return len(p), nil
}

That will give us raw logs, but they may include prefixes and timestamps. Prefixes, if they are used at all, are quite useful; but timestamps are very hard to test for, since you then have to match log entries with regular expressions instead of simple strings.

Fortunately, that too can be overridden. Our overrides then look something like this:

thing := logdemo.New("one")

catcher := &LogCatcher{}
thing.Logger.SetOutput(catcher)

// If you want to test the prefix and output format you can of course
// do that separately.  For testing the written logs it's asking a
// lot, but we can override!
thing.Logger.SetFlags(0)
thing.Logger.SetPrefix("")

// Here we log, and catch it!
thing.Log("here")
assert.Equal("here\n", catcher.Last, "caught first log")

Note that I am using the excellent assert package in order to make unit testing sane. (Again with the rich but infuriating standard library…)

If you’ve read this far and you aren’t totally lost then you can imagine how this continues. But you don’t have to imagine, because the code is there for you on GitHub under biztos/go-demos, specifically as
logdemo.go and logdemo_test.go.

Congratulations, now your logging will not prevent you from achieving the coveted (and utterly spurious) 100% code-coverage metric!

Tuesday, November 03, 2015

Test Package Naming in Go

I’m still – still – playing with Google’s Go language, making the occasional one-off utility and also working on a Crazy Web Server Project. I hesitate to call it a Framework, since all Frameworks are ultimately doomed. It’s nothing too extreme, but it’s (hopefully) going to solve some of my web-server problems for small sites; and it’s (absolutely) helping me learn Go “for reals.”

I recently discovered something very simple, more or less by accident, that has big implications. It is this: tests for package foo should declare package foo_test.

To me this was counterintuitive, since one of the big hairy things you have to adjust to when learning go is the flat package directory. A package is made up of a bunch of .go files, and they all live in one flat directory. If you want to organize the files in a hierarchical set of directories, you must also have a corresponding set of packages (though they need not be hierarchical).

Incidentally, as I get better at Go I find myself hating such conventions less, because they are useful in forcing the programmer’s hand: be idiomatic damn you! And that certainly has its upsides.

Thus, logically, every .go file in a given directory declares the same package. If it doesn’t, you get a compile-time error!

Except, that is, for tests.

The Test Exception

For a directory with a package foo, you may have two packages: foo and foo_test. You may not have any others. So, why would you want to use foo_test?

There is a very good reason, which I’ll get to in a moment. If you already have a lot of unit-testing experience and know a little Go you may already have guessed.

Let’s first consider the way I had been doing it until recently. Apologies in advance for the lack of syntax highlighting.

foo.go

package foo

func Bar() string { return "BAZ"}

foo_test.go


package foo

import "testing"

func Test_Bar(t *testing.T) {

    exp := "BAZ"
    got := Bar()
    if got != exp {
        t.Errorf("Expected '%s', got '%s'", exp, got)
    }
}

OK, great. This makes perfect sense so far. But now consider this, which in fact seems quite the obvious thing to do:

foo.go

package foo

func Bar() string { return ook(1) }

func ook(i int) string {
    if i > 0 {
        return "BAZ"
    } else {
        return "BAT"
    }
}

foo_test.go

package foo

import "testing"

func Test_Bar(t *testing.T) {

    exp := "BAZ"
    got := Bar()
    if got != exp {
        t.Errorf("Expected '%s', got '%s'", exp, got)
    }
}

func Test_ook(t *testing.T) {

    exp0 := "BAT"
    got0 := ook(0)
    if got0 != exp0 {
        t.Errorf("For 0, expected '%s', got '%s'", exp0, got0)
    }

    exp1 := "BAZ"
    got1 := ook(1)
    if got1 != exp1 {
        t.Errorf("For 1, expected '%s', got '%s'", exp1, got1)
    }

}

The example above shows a very common pattern, about whose virtue we could certainly argue: you can have much more thorough test coverage if you have robust unit tests for private functions. (Remember, in Go only Capitalized functions are exported.)

However, there’s a cost in clarity: you should be testing all things that could happen in the use of the package, i.e. you should be testing all variations of the public API. If there are conditions your public API can not trigger, and which thus can not be tested via the public API, then remove them from the code.

Go is strongly biased towards writing only the code that is actually used. Speculative code, while possible, is definitely not idiomatic.

As somebody who writes code for a lot of “what-if” scenarios in Perl, I can certainly apprecitate that discipline.

This is why you should use the foo_test package: Go will then throw a compile time error if you try to test a private function, and in your quest for 100% code coverage you will see the unreachable parts of your code.

So let’s get back to our example, changing the test first:

foo_test.go

package foo_test

import (
    "./"
    "testing"
)

func Test_Bar(t *testing.T) {

    exp := "BAZ"
    got := foo.Bar()
    if got != exp {
        t.Errorf("Expected '%s', got '%s'", exp, got)
    }
}

If Bar is the only thing that ever calls ook then you’ll have to change ook or you’ll never get 100% coverage. In this contrived example, the compiler will most likely abstract away ook entirely, but bear with me.

foo.go

package foo

func Bar() string { return ook() }

func ook() string {
    return "BAZ"
}

And there we have it, in silly little examples. Always use a _test package name for your Go tests. Testing will be harder. Deal with it: your tests will also be better.

Sunday, November 01, 2015

Never Pay Marhaba In Advance.

I recently flew to lovely, hazy Singapore, with a stopover in Dubai. It was a trip of many firsts: my first time in Asia, let alone Singapore; my first time flying Emirates; my first flight on an Airbus A380; and my first time in the famous Dubai Airport.

Since I had arranged for a four-hour stopover, I thought it would be nice to kill the time in a lounge. Since my lovely wife and I were traveling coach – not half bad on the A380 – the business lounges were out of the question. But fortunately there was a pay lounge avaialable: Marhaba.

After first getting a bite to eat elsewhere, we went to the Marhaba desk and asked about buying in. The attendants were quite friendly, and told us the lounge was rather full and thus we might not have a seat. I looked around, and while it was indeed full, there were some bistro-style chairs at a table. Good enough. We paid about 100 EUR for the two of us, and enjoyed the lounge.

I was planning on recommending it: it’s not as nice as a real business lounge, but it’s not too expensive and it beats just killing time in the airport. Plus, they mix a strong G & T.

However, the trip home went rather differently, and hence instead of a glowing recommendation I give you this advice:

Never

Pay

Marhaba

In

Advance

Why not, you ask? Wouldn’t it be cheaper that way, you ask?

Well yes, it would be cheaper, except for the part about Marhaba keeping your money if your flight is delayed.

Our flight from Singapore to Dubai was significantly delayed after boarding. We sat on the plane for over two hours waiting for the engines to be replaced, or whatever technical defect they needed to fix (the Captain left us guessing). Then we were further delayed in the air; and just to make sure everyone was in the best possible mood, Emirates canceled the dinner service, at least in Coach.

When we arrived in Dubai, with clearly insufficient time to make our connection, we hustled through security and asked an attendant what we should do. He told us we should hurry. And so we jogged to the gate, and found the plane waiting.

About ten minutes later we were boarded, along with a few slower joggers, and on our way back to Europe.

Marhaba Will Not Refund Your Money.

I had booked the Marhaba lounge, pre-paid at a small discount, back in Singapore this time, as I figured it would be better that way. I had entered my flight number on their web site, so their system was fully aware of the delay.

Once back at a computer, I sent them an e-mail politely reminding them that my flight had been so delayed it would have been physically impossible to go to the lounge before making my connection, and asked them to refund my payment if they had not done so yet. (Honestly I half expected them to have issued a refund already, since they knew about the flight, but then I am a computer programmer.)

Marhaba got back to me promptly and told me that unfortunately, since I failed to cancel the booking eight hours in advance – i.e., since I failed to call them from the air – they would be keeping my money. Have a nice day.

I thought, well, that can’t really be your policy if you’re providing airport services, can it? This must be a pretty common occurrence: flight delayed, services can’t be used, at least issue a credit for the next time, right?

Bear in mind that lounge access is only one of the services Marhaba provides. You can easily shell out hundreds of Euros (thousands of Dirham) on Marhaba services, and then what happens when your flight is delayed, or for that matter canceled?

So I asked for clarification. And I got it:

We regret to inform you that no refund will be processed for a no show booking since any amendments or cancellation should be done at least 10hrs prior to the flight. Please be advised that all no show bookings has no refund.

Never Pay Marhaba In Advance.

Maybe that’s just how they roll in the Emirates. Maybe it’s usually somebody else’s money. Maybe everyone’s rich enough they don’t care about such things.

But if you care about your money, or about the principle of the matter, then it’s imperative that you never pay Marhaba in advance, for anything.

If I fly through Dubai again I might use their walk-in lounge service, since a strong drink is a strong drink, but my credit card shall never again darken the Marahaba website’s door. Neither should yours.

Tuesday, October 27, 2015

Test Post from Classeur

Mostly nothing to see here folks, move along, move along…

This is just a test post from Classeur, which is a very slick in-browser Markdown editor with support for some popular blogging platforms.

I usually compose my blog posts here in Markdown first, then copy the generated HTML, then make a few corrections to get around Blogger’s buggy HTML-to-HTML converter, then paste it into the blogger UI.

Cumbersome, to say the least. Maybe this is a better way.

Also, maybe it’s an easy way to collaborate on content creation. Well, for open-source stuff it would be pretty easy to come up with a workflow, and the price is way right: $5/mo I believe.

But what if you’re, say, the editor of an online newspaper, and you want your writers to compose in Classeur?

The web site suggests you can publish to DropBox, which might be a solution, but that seems to not be implemented yet. Or you can share documents as read-write among Premium users, which would be a solution for getting it as far as the editor. The editor would then need to export it somewhere.

Yes, I realize Real Newspapers would have online workflows for all this, but first: I’m not a Real Newspaper, and second: I bet their workflows really really suck. Oh, and yes, I realize most online publications don’t employ editors, but humor me please.

So let’s assume this workflow is OK and will actually work:

  1. Reporter, who has a Classeur Premium account, writes a story.
  2. Reporter sends its link to the Editor, who also has a Premium account.
  3. Editor edits, if necessary sends it back, and so on until it’s ready.
  4. Editor exports the Markdown source and sticks it in a database, or source control, or whatever.

I like this so far. Not perfect, but the editor is really quite nice, and in my experience it’s not an easy thing to get non-techies to use plain text editors, much less parse Markdown in their minds.

The problem, though, is in the pictures. I can add a nice picture of a squash ballsquash ballsquash ball here:

sqaush!

The problem is that it’s on Imgur, an image hosting site very popular with the young and bored. And not a place I want my internet-breaking Paparazzi shots of the Donald.

But hey, if I’m a literary editor maybe I don’t need pictures. And if I’m just running some random click -baiting Bored Wombat site, maybe I don’t care.

Again: nothing to see here, test post mostly, &c., YMMV, E&OE, and the like. But I think Classeur is pretty interesting, and I will keep it in mind for future use.

Thursday, June 25, 2015

Go for Minor Utilities

I continue to play around with Google’s Go language, and I continue to have very mixed feelings about it.

It’s clearly not built for You and Me, i.e. for the programmers of the world; rather it’s built for Google and shared with us. This is fine as far as it goes, but as with so many other Google products the joy of the user is mostly “out of scope.”

Also, presumably because the whole Go ecosystem is so young, there are all kinds of quirky things being done in the many, many, many third-party packages scattered around the Githubbernets. I may write more later about the Goverse’s tendency towards Database Worst Practices and its shunning of the Null. (Ironic for a language in which every thirteenth line is a nil check.)

So far, I’m pretty sure I would not use Go for any big, complex, long-term project. But it’s good enough at some things that I’d definitely consider it for any straightforward, easy-to-run subcomponents of a big project. I wouldn’t choose Go for code shared with a bunch of other developers, but if I were dropped into such a project I’d probably do just fine. I might use Go if I planned on bringing in outside help, because as others have pointed out, one of the Google use-cases it seems optimized for is bringing inexperienced programmers into a project.

But there is one thing for which I absolutely love Go, and for which I could picture myself using it for a long time to come: Go is really great for writing simple utility programs.

Tonight I found myself needing some random-ish strings for a goofy little side-project I’m doing. After not finding quite what I wanted at the otherwise wonderful Random.org, I took an hour to throw together a little utility. It will probably only ever be utilitous to me, and I may or may not bother cleaning it up and testing it, but now at least it exists:

https://github.com/biztos/randstr

And that means I can, should I need to on some other computer, simply do this:

$ go get github.com/biztos/randstr
$ which randstr
/Users/Shared/gocode/bin/randstr
$ randstr --help

…assuming my $GOPATH is in order, and subject to dependencies I have not yet “vendored in,” and so on.

There are, I think, four things that make it especially attractive to write such programs in Go:

  1. It compiles, quickly no less, into a single binary.
  2. It’s reasonably cross-platform.
  3. The standard library, while sometimes bizarre, is very rich.
  4. Docopt!

The Joy of DocOpt

Command-line utilities, and Unix-like programs in general, usually take options and other arguments, and they usually have some rudimentary help text explaining these things.

DocOpt parses a structured, but very human-readable, help text in order to determine what the available options are. It’s not perfect but it more than covers the reasonable use-cases of most utilities I’ll ever need to write.

For any nerds who have not yet heard of DocOpt, here is the canonical example:

Naval Fate.

Usage:
  naval_fate ship new <name>...
  naval_fate ship <name> move <x> <y> [--speed=<kn>]
  naval_fate ship shoot <x> <y>
  naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
  naval_fate -h | --help
  naval_fate --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.

There are many implementations of DocOpt, but the one I have been using with Go is the excellent docopt-go by Keith Batten et al.

I do still love the completeness and flexibility of Perl’s Getopt::Long, and I’ve not found anything similar for Go; but now that I’ve gotten used to DocOpt I would think twice before committing to anything more complicated.

With DocOpt in your toolkit, Go is an awesome language for writing quick little utilities. Whatever else it may be, and whatever it may lack, this is reason enough to know some Go.

Monday, February 02, 2015

Go First Impressions

Over the last couple of weeks I’ve been playing with Go, an open-source programming language from Google.

My motivation was pretty simple: I felt like trying out a new server language, something modern and perhaps even trendy. Partly because it’s always a good idea to learn new things, and partly because I was growing frustrated with the limitations of Perl and Javascript/Node. Those are the two languages I use for most of my work, professional and otherwise. While I’d happily sing the praises of either one for many, many domains, every language has its baggage and I was lately feeling the weight of theirs.

Also, a highly respected member of the Perl community recently published a soul-searching article, The Mid-Career Crisis of the Perl Programmer. One thing it reminded me of is that I never considered myself a “Perl Programmer” any more than I consider myself an “Oil Painter.” I earn my keep as a software engineer. I have certain preferences and biases as would any serious professional, but I’d much sooner go to war over text editors than programming languages.

When I look into the crystal ball at my coding future, I see some Perl and some SQL (since 1994...) and that sure looks like Swift out there past the liquor store, but beyond that it’s a little fuzzy as we get closer to the beach. Maybe one of the fuzzies is a gopher, maybe not.

Without further ado, my first impressions of Go follow below.

Pro Go

Productivity Now!

I’m very impressed with how quickly I was able to be at least minimally productive in Go.

When trying out a new language, like most people I want to see if I can use it for something in the real world. In this case I tried a simple web application, as I’ve been writing a bunch of those lately.

A web app should be easy to prototype in this day and age, but it should also be obvious how you can go deep and scale up to a complex and high-performance system. That said, it doesn’t exercise features of the language so much as the available libraries.

After a week of part-time hacking and quite a bit of reading around the web, I feel like I know how to build a robust web app using Go and some of the popular libraries; but also, and here is where Go stands out, I’m fairly confident I could do the same with just the standard library. We’ll soon see why that’s important, but it’s worth noting that I couldn’t do the equivalent in Perl or Javascript without a lot of brute force, and I’ve been writing in those languages since before some Silicon Valley billionaires got to high school.

In fact I can’t remember the last time I played with a new language and so quickly found myself able to write real software, software I understand, and not some Thy-CRUD-Be-Thus templated Sinatrail tiger-trap.

This is a big deal: you might need to hire people, and assuming you are not an idiot you will probably hire people who don’t yet have expertise in the particular software stack you’re using, because that’s not a proxy for intelligence or productivity. (Hiring that way is, as we say in the coding trenches, an anti-pattern, like hiring only Ivy Leaguers or only those who can solve math puzzles in an interview.) These smart people you’ve hired need to get “up to speed” quickly, and it looks to me like Go is a language that would give one a leg up in that inevitable struggle.

The main criticism of Go I’ve encountered so far on the Web is that it may be a practical and a useful language, but it is also ugly and lacks this or that feature. This sounds about right, but for me ugliness exists on a sliding scale: show me the language that gets things done and I’ll show you the language that’s ugly and lacking some feature. Productivity is its own kind of beauty, at least for us Engineers.

Static Types.

As a Perl guy I didn’t expect to find myself saying this, but here it is: I’m starting to prefer static typing. It’s harder to do some things that way, but when I think of all the boilerplate crap I have to put into my dynamically typed code to make it reasonably safe, and all the tests I have to write to make sure I didn’t screw it up, static types start to look pretty good. In my experiments with Swift the static types have been more helpful than not.

And it’s not like you can’t deal with arbitrary types when you need to. Go makes it easy to write things like func DoSomething(m interface{}) {...} – though of course it would be a bad habit to do that very often.

What I’ve grown to like about statically typed languages is what we might call the enforcement of intent: if I write a function to turn Gophers into Marmots then the compiler or interpreter, by default, will make sure that I only ever give it gophers and that I know marmots, specifically, will be returned. This is probably as valuable for its encouragement of design clarity as for its bug-reduction potential.

Readability.

I wouldn’t say Go is a particularly beautiful language, in an aesthetic sense. But it is fairly expressive and once you get used to a few quirks it’s quite easy to read. This is obviously a key part of productivity: every time you write public static void or my ($self,$params) = @_ a poet dies.

You do have bad things, things that are ugly and non-obvious and will be the object of ridicule when Go is a bit older: map[string]reflect.Type is, for example, a perfectly reasonable return value in Go. But most of it is fairly obvious and easily grokable if you’ve done a bit of programming already. (And if you have not, maybe you should see the turtle before you talk to the gopher.)

Compiling and Statically Linking.

This is another one of those things that might sound odd coming from a Perl guy, but when I think about deploying Go programs on servers – possibly on very many servers – I really like the idea that my program, including all its dependencies, will be compiled into a single binary file.

That’s probably not the only file I’d have to deploy, but it removes a lot of hassle from my deployment story, whether that’s just me deploying to a Linode or a team of IT operations experts deploying to a server farm on SeaLand.

Sure, this introduces the problem that you need to compile on the same platform you intend to deploy on; but isn’t that what virtual machines are for? And if you’re not already testing on your destination platform then you’re not really testing, so to my mind this is the smallest of inconveniences. Even for a one-off little project you ought to ssh into your host for a final compile-and-test anyway.

Big Fat Standard Library.

One of the great strengths of Perl is CPAN, a vast repository of free software that will save you immeasurable effort in writing your programs. Go has nothing quite like that (see below); but it does have a very deep standard library. This means you can:

  1. Build a lot without external requirements.
  2. Have a common frame of reference in talking about Go programming.
  3. Fall back to a reasonable midpoint if you find an external package buggy.

The frame of reference might be the most important part of this. If I’m building a web server I can use the net/http package and chances are very good I will be able to get help, should I need it, on sites like Stack Overflow. Because just about everyone who “knows Go” will know how net/http works.

Building software with fewer external requirements can be a great advantage in itself, simply because every dependency introduces a risk of bugs. If I find something genuinely wrong with net/http I am very confident it will get fixed, and that the fix will then be part of the standard software. If I find something wrong with github.com/biztos/gogogo/http then maybe the author will fix it, or maybe not; or maybe I’ll fork it, or maybe somebody else already forked it, or maybe I’ll just throw some sand into the wind and see where it lands, which from a stability point of view isn’t much less useful.

It’s also nice to know you can fall back if you need to, and not have to fall back all the way to sockets. How far this is realistically possible depends on the domain in which you’re operating, but one could at least bring up a decent web API without much external code beyond the database drivers. This might be irrelevant if you’re building the Twitter-Enabled Platform for Instagram Drivesharing (TEPID.IO, YC16). But if you’re stuck in that old-school world of solving actual problems with software it should help you sleep at night.

Scalable and Fast (they say).

I have not yet done anything at all with the features of Go that should, and purportedly do, make it scalable and fast. Unless you count compiling my code as a minimalist demonstration of speed:

$ go build hello.go
$ time ./hello
Hello world.

real    0m0.005s
use     0m0.001s
sys     0m0.003s
$ time echo Hello world.
Hello world.

real    0m0.000s
user    0m0.000s
sys     0m0.000s 

Hmm... But seriously, there’s a lot of chatter online about Go programs being pretty fast, and until I’m in a position to judge (not likely soon) I will take the Internet’s word for it. In any case it ought to be fast-ish just by virtue of its being compiled, right? Right?

The thing that looks most promising for higher-performance programming in Go is its concurrency model. I have not used this at all yet, but I like the look of it, and the fact that its keyword is go makes me think they gave it some serious thought. In fact it looks so promising that I find myself trying to think up a programming task for which I would need it. Some kind of batch processing I suppose...

Cloud Friendly, and Trendy!

What is cloud friendliness anyway? A buzzword, a buzzphrase; maybe it’s meaningless. But when you are thinking about writing things that might need “cloud scale” it’s worth asking how friendly the “cloud” is to your language. Amazon likes Go. Rackspace likes Go. Heroku tolerates Go, in a friendly sort of way. Google App Engine supports Go, in a tentative sort of way.

The concurrency model should make it fairly straightforward to write programs that scale with the size of a compute host, and that ought to help in cloud-style applications. But in the end the trendiness may be the key here: trendiness gets you support (Heroku is Trendy!) and trendiness gets you free software mashups with other trendy things. (The Cloud is Trendy!)

The other thing trendiness gets you is enthusiasm. This might sound shallow, but at the end of the day you will not be the only one writing your software. Having people excited about working with the latest, trendiest thing will improve their work. And that will make you money.

Standards You Can Live With.

With Perl, there’s always more than one way to do anything, but there are some established best practices for common scenarios. With Javascript, there’s usually more than one way to do anything, and there are dozens of competing practices all claiming to be the best. With Go, as far as I can tell, there is a consensus about the “right” way to do most things.

In coding patterns, the Go folks call this idiomatic programming, by which they mean that which follows the language team’s preferences. This is probably a good thing, but it’s hard to see how to enforce it other than to have an appointed Arbiter of the Idiom. That can work in open-source projects, where the Go Community will happily play that role. Internal projects are another story. As far as I know there is no equivalent of PerlCritic, but that hardly puts Go at a disadvantage to any language other than Perl.

Other standards are more actionable. For instance, unit testing is to be done with the built-in testing software. Code formatting is to be done with the gofmt command – a pale, sickly shadow of Perltidy but one that for better or worse brooks no argument.

Project layout also follows a standard, but it’s a very problematic one as I’ll discuss below.

In the end there is a “Go Way” and you can save a lot of time by just submitting to it instead of arguing about its merits. When this works, it’s probably worth having it not be perfect. I’m right about indentation and they are wrong, but if I have to prove I’m right to one more f---ing nerd then I might as well save the effort and live with their wrong indentation. When it doesn’t work, however, it leads to nonstandard standards, which is a slippery slope indeed.

Plan Nine!

Once upon a time, we computer types thought the world needed new and better operating systems. It turned out the world didn’t exactly need them: the Apple computer I’m writing this on, and the server rendering it for you, and the iPhone on which I’m listening to music right now, are all running their fancy software on top of UNIX, which is venerable and beautiful and stable and definitely not modern. Windows limps along apace.

Before the rise of Linux and Steve Jobs’ return to Apple, people really did think there would be something new running the world’s computers by 2015. One of the most exiting new things was Plan 9, named after a classic low-budget sci-fi movie.

It was brilliant and advanced and promising and it looked a lot like the future. But Linux rose, Steve Jobs used NextStep to save Apple, Microsoft stayed in business, and new operating systems like Plan 9 didn’t come to much.

But it was a great idea. And some of the same people who made Plan 9 are behind Go.

No Go

Living with Half-Baked Standards.

I’m sure there are others, but so far I’ve repeatedly been stymied by two poorly thought-out Go standards: project layout and packaging. The standard is in both cases so oddly incomplete that I wonder about the bubble in which it must have been created. Nice, comfortable bubble, no mess here, ah the purity of the bubble...

Project Layout

Go projects are supposed to live in workspaces, and a workspace has a few standard directories: src, pkg, and bin, with pkg holding compiled packages so you don’t have to recompile every last thing every time. You keep your workspaces in your $GOPATH environment variable, which supports multiple workspaces. Multiple workspaces can be useful in development if you want all your third-party packages in one place.

Here for example is a workspace in which I’ve built and installed the foo package, which has a foo/bar package within it and uses one external package, github.com/kr/text:

bin/foo
pkg/darwin_amd64/
                foo/boo.a
                github.com/kr/text.a
src/
    foo/
        main.go
        boo/booing.go
    github.com/kr/text/
                      .git/[shit-ton of git stuff]
                      License
                      Readme
                      doc.go
                      indent.go
                      ...et al. 

You will have noticed that the License and Readme files are thrown in with the source code, which is a bit messy but not unreasonable. And the shit-ton of git stuff is there because I fetched the text package with the standard go get command, and that clones the git repo.

So far so good, but what happens when I have a Real Life Project, in which I have a bunch of things that are essential but are not source code? Templates, configuration files, resources, test data. Where should it live?

Obviously I should have something like this, more or less, for a simple web app:

src/foo/main.go
src/bar/some/other/package.go
templates/index.tmpl
static/foo.css
config/foo-dev.conf 

As far as I can tell, that’s not what Go wants. Go wants me to do this, which is not wise:

src/foo/main.go
src/bar/some/other/package.go
src/foo/templates/index.tmpl
src/foo/static/foo.css
src/foo/config/foo-dev.conf 

I have yet to encounter any defense of this craziness, nor any official way to sanely lay out real-world projects in which the source code is but one part among many. I’m an optimist so I’ll keep looking, but so far it looks like it’s up to me to hack my way around Go’s deficient workspace concept.

If I’m lucky somebody smarter than me will come up with a great solution and it will become the de-facto standard. This often happens when somebody writes a popular Web framework and ships it with a sample application and/or an application generator, the default layout of which is carried forward by thousands of people using the framework.

But if we’re going to look for de-facto standards to emerge from the community, and in the meantime roll our own, then what is the point of having an Official Standard?

Packaging

Go’s default package manager is simply the go get command, which fetches a package from a public repository, does any necessary unpacking and installing, and drops the results under the first directory in your $GOPATH. It’s very clever about cloning Git repositories, and thus gives you a lot of flexibility when dealing with GitHub-hosted open-source packages, which seem to be the majority.

This is great up to a point. That point would be when you want to ship software that’s supposed to work, the not-working of which would be your problem, and which has external dependencies. Then suddenly you realize – oh shit! – you forgot to specify the exact version of that cool parser package from GitHub. And it’s at 0.31 so even assuming malice and human error do not exist in the world, your code still might break when cool/parser hits 0.33 and the API changes.

I keep hoping I will run across some justification for this, since it’s such an obviously bad design yet Go was written by obviously smart people. I have read that “stable code on HEAD” is the answer, but whoever says that doesn’t understand the question. It’s not about hygiene, it’s about predictability. The official justification isn’t much help either.

My best guess is that Go was written in a context, in-house software development at Google, in which package versioning was not an issue, and as a result the authors simply didn’t think of it.

Standard Anarchy.

One of the wonderful things about the open-source world, especially in this age of cheap internet infrastructure, is the way problems are often solved for you before you are even aware of them.

In the case of Go, we have GoPkg.cc, which very elegantly solves the package versioning problem for packages hosted on GitHub (the majority, I believe).

We also have GoDoc for package discovery and documentation. Between these two things Go almost has a CPAN.

I get the sense that Go revels in its anarchy at the moment, while also trying to insist that there are standard ways of doing most things. Presumably something like GoPkg will become a standard, de-facto or otherwise, in time.

The problem I see with this mix of anarchy and imperative is that not everyone is going to agree on the original standards. This problem grows with the user base, particularly as older, more experienced (and more opinionated) programmers get involved. The standard documentation, for example, is ugly and hard to use. Let’s say I make a better documentation package: should I refrain? Should the best solution win, or the factory standard?

Much has been made of the time saved by having gofmt format source code in the One True Standard Way, thus obviating the need to argue about indentation. But if there’s no standard manager for versioned external packages, and no standard Posix-compliant option parser, then why shouldn’t I make a competing code formatter? Let’s say I do, and it catches on. This could be a very good thing, but reduces the point of gofmt to simply saying “automated code formatters are good.” We’ve been saying that in the Perl world for a long long time.

Objects, Not Objects, Deja Vu!

I almost put this in the Pro Go section, because after half a million years writing object-oriented code I welcome the challenge of not thinking in “OO” terms, and I very much look forward to learning more about good objectless design. And anyway Go is kinda-sorta object-y to begin with.

In the real world, however, somebody will devise a clever hack that messily glues object inheritance onto Go’s structs and interfaces, which is where its non-inheriting yes-and-no object-orientation already lives.

In the real world, this object hack will be derided as nonsense by Go purists, will be more or less disowned by the Go community, will be exposed as borderline fraudulent in six dozen blog posts and half that many conference presentations, and will nonetheless be used, largely out of sight, in thousands of Go projects.

I think it was a big mistake of the Go authors to not build in proper object orientation. I don’t see why they couldn’t have. I get that they don’t want people to write Go that way, but sadly I believe a lot of people eventually will; and there won’t be any official way to do it, so somebody’s unofficial solution will end up as the base of some large and indispensible package that sooner or later everybody, or at least you, has to use.

Google: The Glass is Half.

To paraphrase jricher42’s Reddit comment: Google is an advertising company that realizes it must produce excellent software in order to continue dominating the advertising market.

It would be a little ridiculous to expect such a company to over-commit to any particular technology. If you’re a Go lover, and particularly if you’re one who doesn’t love Java and Python, then you probably wish Google would “Go” all-in and make Go their primary language. Then the outside world would love Go even more, because after all it’s good enough to run Google; and Google would get a bunch of free training for potential engineering hires.

But that’s not how this works, and for Go’s sake that’s mostly a good thing. First, given that Google’s support of Go is a little tepid even now, I wouldn’t assume it has any special prominence in Mountain View. As recently as last year I had a Google recruiter pitch me without once mentioning Go: he said I could code in the language of my choice as long as it was Java or Python. Second, Google for obvious reasons insists on the flexibility to discard its experiments, no matter how much “the community” might care about them (viz. Reader), and for that reason alone it’s better if Go isn’t a “Google Thing” per se. Third, Google is every bit as closed-source and poker-faced as Apple when it comes to any software that actually makes it money. If Google comes to depend on Go, then the internal Google fork of Go will start growing proprietary features if it hasn’t already. Good for Google, bad for Go.

This adds up to a risk, and one that we must hope Go outgrows. Google is capricious, and exerts an influence on the language. It might overcommit, it might meddle, it might abandon all support of Go; and in each of these cases the language might suffer, to an unknown degree.

Youth Itself.

Go is a very young language. It’s been publicly available since November 2009, which makes it five years old as of this writing.

Five years is time enough to mature some but not really time enough to prove any staying power. These have been faddish years in computer languages, and while Go’s popularity is growing it’s hard to guess how far it’ll, er, go.

Consider Perl: so very not trendy, yet so well established that an enormous amount of very serious code is written in it every day, making a lot of people a lot of money (and the programmers some too). Most of it isn’t powering startups, and I suppose none of it is powering Google, but Perl remains a reliable industry workhorse. I would much sooner trust Perl’s DBI than anything gluing databases onto Go.

Will Go become that reliable? Will an investment in Go expertise pay dividends in ten years? In twenty? Will there be a canonical Oracle driver, Perforce and Atlassian integration, that sort of thing?

I like Go largely because it solves problems I actually have, and the “canonical Go program” is a web application, of which I expect to write many more in years to come. But Go’s youth is a risk and will be for at least a few years. It would be helpful to hear success stories about large-scale adoptions of Go at major companies other than Google. Are there any such stories?

Limited Sense of Humor.

Much like Google itself, I get the feeling that desipite its cute (?) gopher mascot the Goosphere takes itself a bit too seriously. The Perl folks are much wittier.

Case in point:

$ go ogle
go: unknown subcommand “ogle”
Run ’go help’ for usage. 

Further Reading

If you’re interested in using Go, I recommend you start by playing with it, or following the official tutorial, or reading a free online book. If you’re the learn-by-doing type that will probably be enough to get you excited or repulsed, depending on whether Go is a natural match for you.

I also strongly recommend reading a talk the Go authors published in 2012, Go at Google: Language Design in the Service of Software Engineering. That explains the why of Go as well as much of the how,
and I don’t think the FAQ really makes sense until you’ve read it.

Finally, I found it enlightening to read a few “Go vs X” writeups, such as Scala vs. Go on Quora.

Now go build something!