The Daily Parker

Politics, Weather, Photography, and the Dog

NotPetya was NotRussia, says court

Via Bruce Schneier, the New Jersey Superior Court has found that the NotPetya attack that disabled much of Merck's shipping network in 2017 was not an act of war by the Russian government, and therefore Merck's insurer may be on the hook for a $1.4 billion payout:

The parties disputed whether the Notpetya malware which affected Merck's computers in 2017 was an instrument of the Russian government, so that the War or Hostile Acts exclusion would apply to the loss.

The Court noted that Merck was a sophisticated and knowledgeable party, but there was no indication that the exclusion had been negotiated since it was in standard language. The Court, therefore, applied, under New Jersey law, the doctrine of construction of insurance contracts that gives prevalence to the reasonable expectations of the insured, even in exceptional circumstances when the literal meaning of the policy is plain.

The Court also noted that under New Jersey law, 'all risks' policies extended coverage to risks not usually contemplated by the parties unless a specific provision excluded the loss from coverage.

36 Group's article included the court order from December 6th. The ruling only applies to New Jersey, but I expect insurance companies will take hard looks at all of their "all risks" policies to see how much exposure they could have to another cyberattack. I suspect insurers will start demanding people protect their networks better, too, the way they have encouraged people to buy safer cars.

It might also bankrupt Ace American Insurance Co., but that won't change the follow-on effects of this ruling.

Is the Covid test plan a stealth argument for single-payer? One can dream

New Republic Natalie Shure points out the absolute, crashing idiocy of getting private health insurance companies involved in procuring free Covid testing, because their whole reason for being is to prevent the efficient procurement of health care:

This rollout will be a disaster. And really, that should have been obvious: There’s a reason that the Covid-19 vaccines, monoclonal antibody treatments and antiviral drugs have been made free at the point of use, rather than routed through private insurers. It’s because the insurance industry is structurally incapable of achieving anything universally or efficiently.

That’s not hyperbole, it is by design: The role of private insurers within a for-profit multi-payer system is to restrict access as a gatekeeper, determining who is entitled to use which healthcare services and how much they pay for this. To keep these obligations profitable, they employ an army of claims assessors to argue with you, erect arbitrary hoops for providers and patients to jump through to prove you actually need certain care, raise copays and deductibles as high as possible, and foist as much of the paperwork as possible onto patients.

Insurance companies play the single ghastliest role in a legendarily ghastly healthcare system: Whatever invective you can hurl in Big Pharma’s direction, they at least produce something we actually need. Health insurers offer no value whatsoever; they have nothing to do with care itself and if the industry vaporized completely tomorrow, no one would mourn its demise—we’d all be better off. We’re maddeningly stuck with them for now, owing to a host of reasons ranging from inertia to political capture by industry.

[T]his is an object lesson: We’re in the hands of an industry that was never built to serve patients, a problem which no regulatory tweak will ever fix.

Yes, this is true. I learned that early in my career, leading to a long-standing policy of never working for a health insurer in any capacity.

Let me catalog some of my own experiences when big health-insurance companies have claimed to pay me to write software:

  • The first time, a major health-insurance company hired me to write software using a then-current technology, but the project wasn't ready to start, so they involuntarily put me on a team working with obsolete technology and a process so stultifying I didn't actually get to write code. I literally picked up my signing bonus on the way to several interviews in New York, and quit the day I got back. The entire division got dissolved about a year later.
  • Well into my professional career, I went to another major health-insurance company along with 8 other developers and managers, but under the aegis of a moderately-big consulting firm. The on-site project cost the client of about $150,000 per week. Of course, they couldn't get us network access or even a project charter. After about a month and about $750,000 spent, the company cancelled the project. I never even found out what software they proposed to build, had they gone through with, you know, building it.
  • More recently, a major health-insurance company hired me through a recently-acquired subsidiary as the 6th member of a team writing software in a language less than 3% of software developers ever use. I only took the gig because the subsidiary claimed a level of autonomy from the parent company it did not, in fact, enjoy. It occurred to me less than a week after starting that if the product we were building worked, it would undermine one of the key revenue streams of the parent company. Nevertheless, they hired a new developer to the team about every three weeks (despite an admitted 6-month ramp-up time in the language and product), at an average all-in cost of $18,000 per month per developer. I left after three months, as the team grew past 12 people and yet only completed about 5 function points a week.
    The parent company killed not only the project but also the entire acquired company about two years later. The software never shipped, though I did hear they had completed about half of the planned function points.
    On my way out the door I asked my manager what it said to him that the parent company didn't care about burning $200,000 a month on software that he and I both knew a couple of us could build in a garage in four months. He didn't say anything, but Upton Sinclair did*.

You may not see the connection between these failures, or why I jumped ship so quickly the third time, but it's actually really simple. In all three cases, the companies needed to show their shareholders ongoing investments in technology, and needed to show the general public plans for really great tools to make people's lives better any day now. But the best way any of these companies could have made anyone's lives better would be for the US government to obviate their health divisions by paying for our health care directly.

According to the World Bank, the US spends 17% of GDP on health care, behind only Tuvalu and the Marshall Islands (combined populations: 72,000). The first OECD countries on the list are Switzerland (11.9%), Germany (11.4%), and France (11.3%), all of which have vastly better outcomes than the US. How do they achieve this? By not having fat, bloody leeches draining their health spending on useless bullshit. Example: the National Institutes of Health found in a 2020 study that a staggering 34.2% of health-care expenditures in the US went into administration, compared with just 17% in Canada—and Canada has better health-care outcomes overall than we do.

Shure ends her column with an inescapable truth:

[I]t’s pretty telling that the very moment a life-threatening pandemic necessitated mass vaccination, the idea of involving private health insurance companies with that project was absolutely unthinkable. Who in their right mind would attempt to involve them in something urgent? And if they’re such a dismal way to confer access to Covid-19 testing to anyone who needs it, why the hell are they still playing the role they do in the healthcare system writ large?

Let's end this farce and get real single-payer health care in the US, so we can finally enter the 21st century with the rest of the OECD.

* "It is difficult to get a man to understand a thing when his salary depends on him not understanding it."

Weather Now 5 soft launch

After 15 months of on-and-off work, I'm finally ready to show off Weather Now v5.0, currently in development.

I started building the API and UI projects on top of the core and automation features around Thanksgiving, spending about 45 hours over the past 5 weeks on it. Overall, I've worked about 200 hours to get it to this point, starting with an empty Visual Studio solution file and an empty Microsoft Azure subscription.

Since about 99% of what you can't see already exists, I don't expect it will take me another 15 months to build the stuff you can see. In fact, I only have 21 more JIRA cases (out of 187 so far) to bring it to production. Adding an existing v4 feature to v5 just requires creating the Blazor page, API method, and glue code, which shouldn't take more than 2-3 hours per item, including testing.

Check in frequently. I push updates a few times a week at this point, so you should see pretty rapid changes until I launch the production release (I hope) before the end of March.

First deployment of 2022

All of my apps run on servers that use UTC. As it's now 00:40 UTC, that means the code I just pushed to a dev server will start running on January 1st UTC, which is in fact why I waited until after 6pm to push the code up to DevOps.

It looks like Chicago will get about 150 mm of snow tomorrow during the day, giving me plenty of time to continue my four-day weekend of coding. If I can get a couple of things out of my backlog and onto my dev environment before Sunday night, I may just release the link to what I'm doing so Daily Parker loyalists can start playing with it.

As always, watch this space.

And happy new year!

Productive Sunday

Even though Cassie really wants to go outside right now, I'm going to make her wait another 10 minutes while I push some code and wait for the continuous integration build to run. She doesn't understand that I need to run with productivity when I have it. The closest she gets to understanding that is running with balls when she has them.

OK, pushing 10 commits. Run, you clever CI, and remember...

Hours and hours for a one-line fix

Last week I posted a bug report on an app I'm developing. I couldn't figure out why a nav bar only appeared for logged-in users. Almost 7½ hours of debugging  over a 10-day stretch later, and I figured it out.

It turns out that the default AddAuthorization service provider options blocked a request somewhere, so removing it allowed all the page components to load, even while keeping the authentication-required bits hidden.

builder.Services.AddAuthorization(options =>
{
	// By default, all incoming requests will be authorized according to the default policy
	//options.FallbackPolicy = options.DefaultPolicy; // Commenting this out fixed it
});

That's the fun part of debugging: it's always the last thing you try.

New technology, new weirdness

I've mentioned that Inner Drive Technology will release a new version of Weather Now pretty soon. I've finished everything except the UI and migrating the data, in fact, so I may even finish in January.

I have an odd bug, though, so I'm posting here in addition to the posts I made on Stack Overflow and on the Blazorise GitHub project.

In short, Blazorise speeds up UI development by abstracting away a lot of the formatting and layout for a .NET Blazor app. Blazor, in turn, abstracts away most of the lower-level UI code that makes websites interactive and fast. It's a long way from the XML-XSLT page construction I used in the last Weather Now UI update back in 2007. (Yes, the UI turns 15 soon—but the app itself turned 22 on November 11th.)

Without going too deeply into the issue, let me sum up. The new version will allow users to log in and customize their experience. But it still needs to work for anonymous users, who will make up probably 95% of the users.

The new site will continue the left-side navigation pane for desktop views. To do that, I built a MasterLayout.razor page that looks like the demo code in the Blazorise documentation:

<Layout Sider="true">
	<LayoutSider>
		<LayoutSiderContent>
			<Bar Breakpoint="Breakpoint.Desktop" NavigationBreakpoint="Breakpoint.Tablet" ThemeContrast="ThemeContrast.Dark"
			     Mode="BarMode.VerticalPopout" CollapseMode="BarCollapseMode.Small">
				<BarToggler />
				<BarBrand>
					<BarItem>
						<BarLink To="">
							<BarIcon IconName="_customIcon" />
							Weather Now
						</BarLink>
					</BarItem>
				</BarBrand>
				<NavMenu />
			</Bar>
		</LayoutSiderContent>
	</LayoutSider>

	<Layout>
		<LayoutHeader Fixed="true">
			<Bar @bind-Visible="@_topbarVisible" Breakpoint="Breakpoint.Desktop" Background="Background.Primary" ThemeContrast="ThemeContrast.Light">
				<BarBrand>
					<BarItem>
						<BarLink To="">
							<BarIcon IconName="FontAwesomeIcons.CloudSun" />
							Weather Now
						</BarLink>
					</BarItem>
				</BarBrand>
				<BarMenu Class="justify-content-end">
					<BarEnd>
						<AuthorizeView>
							<Authorized>
								<BarItem>
									<Blazorise.Icon Name="FontAwesomeIcons.User" Visibility="Visibility.Visible" />
									Hi, @context?.User?.Identity?.Name
								</BarItem>
							</Authorized>
						</AuthorizeView>
						<BarItem>
							<LoginDisplay />
						</BarItem>
					</BarEnd>
				</BarMenu>
			</Bar>
		</LayoutHeader>

		<LayoutContent Padding="Padding.Is4.OnX">
			@Body
		</LayoutContent>

		<LayoutFooter Fixed="true" Padding="Padding.Is4.OnX">
			Copyright ©@DateTimeOffset.UtcNow.Year Inner Drive Technology.
		</LayoutFooter>
	</Layout>
</Layout>

The issue is that when a logged-in user views a page, they see the part within the <LayoutSider> tag, which includes the navigation menu. When an anonymous user hits the page, they don't see anything in that area.

The culprit turns out to be the <Bar Mode=""> attribute. If that attribute is present with any value at all, the behavior occurs. Without that value, the behavior does not occur.

One more data point: the Program.cs startup code contains this bit:

builder.Services.AddRazorPages(options =>
{
	options.Conventions.AllowAnonymousToFolder("/");
	options.Conventions.AllowAnonymousToPage("/Index");
	options.Conventions.AllowAnonymousToFolder("/_content");
	options.Conventions.AllowAnonymousToFolder("/Pages");
	options.Conventions.AllowAnonymousToFolder("/Shared");
});

That code lets anonymous users see any content in the app that doesn't specifically require being logged in or authorized.

Anyway, I hope someone in the 'verse sees one of these posts and knows what has gone wrong. (If you do, please comment on the Stack Overflow post so we both get reputation points.)

Backlog

I just started Sprint 52 in my day job, after working right up to the last possible minute yesterday to (unsuccessfully) finish one more story before ending Sprint 51. Then I went to a 3-hour movie that you absolutely must see.

Consequently a few things have backed up over at Inner Drive Technology World Headquarters.

Before I get into that, take a look at this:

That 17.1°C reading at IDTWHQ comes in a shade lower than the official reading at O'Hare of 17.8°, which ties the record high maximum set in 1971. The forecast says it'll hang out here for a few hours before gale-force winds drive the temperature down to more seasonal levels overnight. I've even opened a few windows.

So what else is new?

So what really is new?

But Sprint 52 at my office, that's incredibly new, and I must go back to it.

Coding on a winter afternoon

I've finally resumed progress on a major update to Weather Now. I finished everything except the user interface way back in April, but between summer, Cassie, and everything else, I paused.

At least, until last week, when something clicked in my head, and I started writing again. As my dad would say, I broke the code's back.

It turns out, the APIs really work well, and I'm getting used to .NET Blazor, so I'm actually getting things done. The only downside applies to Cassie, who will probably only get 90 minutes of walks today instead of the two hours or so she got on summer Saturdays.