<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kevin Burke</title>
	<atom:link href="https://kevin.burke.dev/feed/" rel="self" type="application/rss+xml" />
	<link>https://kevin.burke.dev</link>
	<description>The golden age is before us, not behind us</description>
	<lastBuildDate>Thu, 12 Feb 2026 12:36:26 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>
	<item>
		<title>How to fix the NBA Draft</title>
		<link>https://kevin.burke.dev/kevin/how-to-fix-the-nba-draft/</link>
					<comments>https://kevin.burke.dev/kevin/how-to-fix-the-nba-draft/#respond</comments>
		
		<dc:creator><![CDATA[kevin]]></dc:creator>
		<pubDate>Thu, 12 Feb 2026 12:36:26 +0000</pubDate>
				<category><![CDATA[Today's World]]></category>
		<guid isPermaLink="false">https://kevin.burke.dev/?p=3893</guid>

					<description><![CDATA[I read this and then had trouble finding it for a year, I'm writing about it so I have a way to find it... The proposal here is so obviously correct that it's shocking it doesn't come up more often when people discuss the NBA Draft... basically the idea is that you assign a high [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I read this and then had trouble finding it for a year, I'm writing about it so I have a way to find it... The proposal here is so obviously correct that it's shocking it doesn't come up more often when people discuss the NBA Draft... basically the idea is that you assign a high value to wins and losses when everyone is trying to win, and then when people's intentions become more obvious you assign a lower (or sometimes even negative!) value to losses.</p>

<p><a href="https://threadreaderapp.com/thread/1922471157569057117.html">Read the full proposal from economist Kevin Bryan here</a>.</p>

<p>The NBA product has become so degraded and it's so obviously in the league's interest to have teams trying to win games with their best players that it's crazy they're not looking into solutions like this. I hope they will soon.</p>]]></content:encoded>
					
					<wfw:commentRss>https://kevin.burke.dev/kevin/how-to-fix-the-nba-draft/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Speeding up Javascript Test Time 1000x</title>
		<link>https://kevin.burke.dev/kevin/speed-javascript-tests-1000x/</link>
					<comments>https://kevin.burke.dev/kevin/speed-javascript-tests-1000x/#respond</comments>
		
		<dc:creator><![CDATA[kevin]]></dc:creator>
		<pubDate>Thu, 30 Oct 2025 16:44:34 +0000</pubDate>
				<category><![CDATA[Today's World]]></category>
		<guid isPermaLink="false">https://kevin.burke.dev/?p=3886</guid>

					<description><![CDATA[*This post originally appeared on Shyp's engineering blog in July 2015. It has since been deleted. It is reproduced as closely as possible here. The original is accessible on the Wayback Machine. Eight months ago when I ran our core API tests at Shyp, it took 100 seconds from starting the test to seeing output [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>*This post originally appeared on <a href="https://web.archive.org/web/20160304152844/https://shyp.com/engineering">Shyp's engineering blog</a> in July 2015. It has since been deleted. It is reproduced as closely as possible here. The original <a href="https://web.archive.org/web/20160304152844/https://shyp.github.io/2015/07/13/speed-up-your-javascript-tests.html">is accessible on the Wayback Machine</a>.</p>
<p>Eight months ago when I ran our core API tests at <a href="https://techcrunch.com/tag/shyp/">Shyp</a>, it took 100 seconds from starting the test to seeing output on the screen. Today it takes about 100 milliseconds:</p>
<p><img decoding="async" src="/rawblog/images/shyp-tests/fast-test-run.gif" alt="Screenshot of a fast test run" /></p>
<h2>Why Bother?</h2>
<p>There are a lot of different demands on your time, why is a speedy test framework so important? Here are a few reasons we put a premium on being able to run tests quickly.</p>
<ol>
<li>
<p><strong>Deployments get faster.</strong> Our build/deployment server runs every test before a deployment. Making our tests faster means that we spend less time between pushing code and seeing it live in production. In situations where production is broken and we need to push a fix, faster tests mean we can get that fix to production more quickly.</p>
</li>
<li>
<p><strong>Gains accrue over time.</strong> We run tests 400-500 times a day; a ten second improvement in every test run translates to an hour of saved developer time, <em>every day</em>.</p>
</li>
<li>
<p><strong>Slow tests lead to context switches. Context switches are harmful.</strong> Distractions are everywhere with Slack, push notifications and open-plan offices. A ten or twenty second delay between kicking off a test run and viewing the results means you might be tempted to check email or Twitter or do any other activity which causes you to lose focus on the task at hand. Faster tests mean you can stay focused more easily.</p>
<p>Our target is to get feedback within 100ms; that’s <a href="https://web.archive.org/web/20160304152844/http://www.nngroup.com/articles/response-times-3-important-limits/">about as much time as a UI can take before it stops feeling responsive</a> and your mind starts to wander.</p>
</li>
<li>
<p><strong>Fast tests lead to better code.</strong> We are subconsciously biased against things that are slow. <a href="https://web.archive.org/web/20160304152844/http://highscalability.com/latency-everywhere-and-it-costs-you-sales-how-to-crush-it">Amazon famously found that 100ms of latency costs 1% of sales.</a> You might want to refactor a module, but subconsciously decide not to, because refactoring implies running the tests, and the tests are slow. You might not write an extra test you really should write, because it means <em>running the test suite again</em>, and the tests are slow. You might decide <a href="http://yellerapp.com/posts/2015-03-16-incuriosity-killed-the-infrastructure.html">not to be curious about a test anomaly</a>, because narrowing down the issue would require running the tests, and the tests are slow.</p>
</li>
</ol>
<p>For these reasons, it’s important to us that we start to see test output in under 100ms. Fortunately, we were able to hit that goal. How did we do it?</p>
<h2>Measuring Performance</h2>
<p>The first step to making anything faster is to measure how fast or slow it is. At a minimum, you’ll want to measure:</p>
<ul>
<li>
<p>How long it takes before the first test starts running</p>
</li>
<li>
<p>How long it takes to perform any global, per-test setup/teardown actions</p>
</li>
<li>
<p>Minimum amount of time to do a database read (for us, 4ms)</p>
</li>
<li>
<p>How long each test takes. If you run your tests with <a href="http://mochajs.org/"><code>mocha</code></a>, I encourage you to set the <code>--slow</code> flag to 2 (milliseconds) so you can clearly see how long each test takes to run.</p>
</li>
</ul>
<p>There’s <a href="https://unix.stackexchange.com/a/26797/9519">an awesome Unix tool called <code>ts</code></a> (available on Macs via <code>brew install moreutils</code>) that will annotate every line of your test output with a timestamp. Pipe your test output through <code>ts</code> like so:</p>
<pre class="chroma"><code><span class="line"><span class="cl">mocha test/api/responses/notFound.test.js <span class="p">|</span> ts <span class="s1">&#39;[%Y-%m-%d %H:%M:%.S]&#39;</span>
</span></span></code></pre>
<p>And you’ll get test output annotated with timestamps with millisecond precision; all you need to do is find the right place to put <code>console.log</code> lines.</p>
<pre class="chroma"><code><span class="line"><span class="cl"><span class="o">[</span>2015-04-19 21:53:45.730679<span class="o">]</span> verbose: request hook loaded successfully.
</span></span><span class="line"><span class="cl"><span class="o">[</span>2015-04-19 21:53:45.731032<span class="o">]</span> verbose: Loading the app<span class="err">&#39;</span>s models and adapters...
</span></span><span class="line"><span class="cl"><span class="o">[</span>2015-04-19 21:53:45.731095<span class="o">]</span> verbose: Loading app models...
</span></span><span class="line"><span class="cl"><span class="o">[</span>2015-04-19 21:53:47.928104<span class="o">]</span> verbose: Loading app adapters...
</span></span><span class="line"><span class="cl"><span class="o">[</span>2015-04-19 21:53:47.929343<span class="o">]</span> verbose: Loading blueprint middleware...
</span></span></code></pre>
<p>We observed right away that a) our Node framework requires a <em>ton</em> of files before starting a test run, b) <code>require</code> is synchronous, and <em>really</em> slow in Node (on the order of milliseconds per file imported), and c) There were a <em>ton</em> of stat() syscalls to try and load modules in places where those modules did not exist.</p>
<p>The latter problem is <a href="https://kevin.burke.dev/kevin/node-require-is-dog-slow/">documented in more detail on my personal blog</a>, and there have been two promising developments in that area. First, <a href="http://glebbahmutov.com/">Gleb Bahmutov</a> developed <a href="https://github.com/bahmutov/cache-require-paths">the <code>cache-require-paths</code> library</a>, which helps Node remember where it found a file the last time it was imported, and avoids many wasteful/incorrect file seeks. Gleb observed a 36-38% speedup when loading an Express project - our speedup was closer to 20%, but we are still really glad this tool exists.</p>
<p>Second, <a href="https://github.com/pierreinglebert">Pierre Ingelbert</a> <a href="https://github.com/nodejs/io.js/pull/1920">submitted a patch</a> to <code>io.js</code> to avoid extraneous stat() syscalls in directories that do not exist. This patch was part of the io.js 2.3.1 release.</p>
<h2>Loading Shared Folders Kills Performance</h2>
<p>We run our tests in a virtual machine, so our development environment matches production. The core <code>api</code> project is shared between the host machine (usually a Mac) and the VM. Loading the test suite means that a lot of files in the <code>node_modules</code> directory are being loaded. If that <code>node_modules</code> folder lives inside the folder that’s being shared, the VM will have to reach across the system boundary to read it, which is much slower than reading a file inside the virtual machine.</p>
<p>It was clear we needed to install our <code>node_modules</code> folder somewhere inside the VM, but outside of the shared folder. Node’s <code>NODE_PATH</code> environment variable provides a mechanism for loading a folder saved elsewhere on the filesystem, but our Javascript framework <a href="https://github.com/balderdashy/sails/issues/2505">hard-codes the location of the <code>node_modules</code> folder</a> in its imports, so it failed to find the files we had placed elsewhere.</p>
<p>Instead we installed the <code>node_modules</code> folder elsewhere and symlinked it into place. Here’s a bash snippet you can use to replicate this behavior.</p>
<pre class="chroma"><code><span class="line"><span class="cl"><span class="nb">pushd</span> ~/api
</span></span><span class="line"><span class="cl">    npm install --prefix /opt/lib/node_modules
</span></span><span class="line"><span class="cl">    ln -s /opt/lib/node_modules ~/api/node_modules
</span></span><span class="line"><span class="cl"><span class="nb">popd</span>
</span></span></code></pre>
<p><em>Savings</em>: This sped up test initialization time by <strong>a whopping 75%</strong>, or one minute and fifteen seconds.</p>
<h2>More Specific Regex</h2>
<p>We used to specify a test to run by typing <code>mocha -g 'testname'</code> at the command line. Unfortunately, mocha always loads a <code>mocha.opts</code> file if it is present, and our <code>mocha.opts</code> file hard coded a filename regex that matched every single test file in our system (100+ files). We added a new test runner and instructed people to manually specify the test file they want to run.</p>
<p><em>Savings</em>: This sped up test run time by about 50% (10-13 seconds).</p>
<h2>Stubbed database reads/writes</h2>
<p>The old test framework would do authentication by writing a valid access token to the database, then reading/returning the valid access token in the controller. We introduced a synchronous <code>signIn</code> test helper that stubbed these two network calls.</p>
<p><em>Savings</em>: 10-20ms across ~600 tests, a 6-12 second improvement.</p>
<h2>Batched writes</h2>
<p>In some instances we need to instantiate test environments with users, drivers and other objects. Previously the test helper would write one record, read the id, and then write a record that depended on it. By generating ids up front, we were able to perform multiple writes at the same time.</p>
<p><em>Savings</em>: 10-20ms across ~200 tests, a 2-4 second improvement.</p>
<h2>Faster test cleanup</h2>
<p>Between each test, we delete all records from the database. The helper responsible for this would open one database connection per table and then each one would call <code>DELETE FROM &lt;tablename&gt;</code>. Not only would this occasionally hang with no stack trace, it meant that the speed of the cleanup operation was the same as the slowest DELETE query.</p>
<p>Instead we grouped all of the deletes and sent them to the database in a single connection (e.g. <code>Model.query(&quot;DELETE FROM users; DELETE FROM pickups; ...&quot;)</code>). Some reading online indicated <a href="https://stackoverflow.com/a/11423886/329700">TRUNCATE would be faster than DELETE</a>; plus, it lets you use one command for everything, e.g. <code>TRUNCATE TABLE users, pickups, ...</code>. For large datasets it is likely faster, however we observed this to be much slower than DELETE for our small test data sets, on the order of 200ms per action.</p>
<p>An optimization we’d like to implement in the future would only issue DELETEs for tables that have dirty data, which would also let us avoid issuing a DELETE if a test didn’t hit the database. Currently we’re not sure about the best way to hook into the ORM and determine this.</p>
<p>We’re also interested in running every test in a transaction. Unfortunately the ORM we use <a href="https://github.com/balderdashy/waterline/issues/755">doesn’t support transactions</a>, and we are very worried about upgrading it.</p>
<p><em>Savings</em>: Clearing the DB used to take 14-30ms per test, now takes 3-11ms, a ~20 second improvement.</p>
<h2>Don’t Load Sails</h2>
<p>Our Javascript framework (Sails.js) needs to require every single model, controller, and service before it runs; this is the slowest part of the test run by far.</p>
<p>Where possible, we try to avoid loading Sails when writing/running tests. We try to implement most of our business logic in plain Javascript classes or modules, outside of models and controllers. If this logic operates on objects in memory, you can use fake model objects in your tests - <code>var user = {email: 'foo@bar.com'}</code>, and avoid loading Sails entirely.</p>
<p>Avoiding Sails isn’t possible in every situation, but we’ve found it’s a useful design principle - it helps us separate our business logic and our data storage layer, and will make it easier for us to move off Sails in the future.</p>
<p>For more on this technique, you should check out <a href="https://www.destroyallsoftware.com/screencasts">Gary Bernhardt’s excellent <em>Destroy All Software</em> series of videos</a>. He gave <a href="https://www.youtube.com/watch?v=RAxiiRPHS9k">a great introduction to his “don’t load Rails” style of testing at Pycon in 2012</a> (summary <a href="https://web.archive.org/web/20160304152844/http://pycon-2012-notes.readthedocs.org/en/latest/fast_tests_slow_tests.html">here</a>).</p>
<h2>Tighter Editor Integration</h2>
<p>Most of the time the test you want to run is the same one that is right underneath your cursor, so manually specifying the filename at the command line wastes time. The awesome <a href="https://github.com/vim-test/vim-test"><code>vim-test</code> plugin</a> makes it incredibly easy to map leader commands for running the current suite of tests or the current file. It worked on literally the first try; I’ve rarely been so impressed with a piece of software.</p>
<p>Some of our team members use <a href="http://www.sublimetext.com/">Sublime Text</a>, and we haven’t figured out how to get their editor integration set up yet. It’s on our to do list for this quarter.</p>
<p><em>Savings</em>: One context switch and ~1-3 seconds per test run.</p>
<h2>Avoid Reloading All Dependencies Every Test Run</h2>
<p>After all of this we were able to get test run time down to about 7 seconds. Most of this time is spent waiting for v8 to parse/require ~500 files.</p>
<p>We can go much faster if we loaded every dependency once when we sat down at the computer, and then only re-required the files that had changed. You can do this by hacking with Node’s <code>require.cache</code>. I don’t recommend it for production, but it works just fine.</p>
<p>Anyway the end result of this is <a href="https://web.archive.org/web/20160304152844/https://github.com/Shyp/lucifer">a command line client and a server called Lucifer</a>. Start Lucifer when you sit down at your computer. When we change a file, we call <code>lucifer invalidate [filename]</code> (or configure our editors to do this for us), which invalidates the cache for that file, and then re-requires it. Then you can kick off a test run by calling <code>lucifer run [filename]</code> from the command line (or from your editor). When you kick off a test every dependency is ready to go, which means your tests start in about 100ms.</p>
<p><img decoding="async" src="/rawblog/images/shyp-tests/fast-test-run.gif" alt="Screenshot of a fast test run" /></p>
<p><em>Savings</em>: Test initialization time went from 6-7 seconds to 100ms, a 60x speedup.</p>
<h3>Caveats</h3>
<p>This is similar to the approach taken by <a href="hhttps://github.com/sporkrb/spork">spork</a> in Ruby - keep a server up and reload changed files. The same failure modes that apply to Spork - increased complexity, subtle inconsistencies, failure to reload files - apply here. In particular, we’ve found that sometimes Node doesn’t wipe the cache for files that have changed, so new code won’t get loaded. This is probably a result of our limited understanding of how and where Node caches loaded dependencies. We always do a clean run of our test suite on our build server before deploying to production.</p>
<p>Still, it’s been very useful in certain situations - if you’ve flushed out a method or a controller and are writing several tests in quick succession, the only file that’s changing is your test file, so the chance of subtle dependency breakage is low.</p>
<h2>Lessons</h2>
<p><em>Your slow test suite isn’t hopeless!</em> But improving it is probably going to take a concerted investment. You also have to get know your stack really well, which is probably a good idea anyway. Look at all of the places we had to inspect to find performance improvements:</p>
<ul>
<li>File reads in a virtual machine</li>
<li>The <code>require</code> function in Node</li>
<li>Test workflow</li>
<li>Use batching to avoid database connection overhead</li>
<li>Editor integration</li>
</ul>
<p>You don’t know what will be slow until you measure it. You’ll also want to know how to use basic profiling tools for your stack:</p>
<ul>
<li><a href="http://unix.stackexchange.com/a/26797/9519">the ts command</a></li>
<li>Strace/DTruss (not covered in depth here, but extremely useful for observing system calls/timings for a running process)</li>
<li>Logging / timing for queries that hit the database</li>
<li>Logging and profiling your test run time</li>
</ul>
<h2>We’re hiring!</h2>
<p>Our team of ten engineers powers four warehouses, four mobile apps, hundreds of drivers and loads of pickups every day. We’re looking for people who are curious about the tools they use and eager to improve their productivity day in and day out. <a href="https://web.archive.org/web/20160304152844/https://shyp.com/jobs">We’d love to hear from you</a>; you can’t waste our time by getting in touch.</p>
<h2>Errata</h2>
<p>Some odd things I found while profiling:</p>
<ul>
<li>Accessing the <code>.stack</code> property of an Error object <a href="https://groups.google.com/forum/#!searchin/nodejs/stack$20slow/nodejs/-U2hIDWcc30/5WRuCeoA8HgJ">can block a process for up to 100ms</a>. Stub your logger before running tests and you can see a quick win.</li>
<li>Some imports are extremely slow; <code>require('faker')</code>, for example, <a href="https://github.com/Marak/faker.js/issues/167">tacks a 60ms penalty onto your test runs</a>. If you don’t need the locale data, you can speed this up quite a bit by only loading locales that you need.</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://kevin.burke.dev/kevin/speed-javascript-tests-1000x/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Let Us Open URLs in a Specific Browser Profile</title>
		<link>https://kevin.burke.dev/kevin/open-urls-in-specific-browser-profile/</link>
					<comments>https://kevin.burke.dev/kevin/open-urls-in-specific-browser-profile/#respond</comments>
		
		<dc:creator><![CDATA[kevin]]></dc:creator>
		<pubDate>Wed, 29 Oct 2025 17:00:27 +0000</pubDate>
				<category><![CDATA[Code]]></category>
		<guid isPermaLink="false">https://kevin.burke.dev/?p=3881</guid>

					<description><![CDATA[Most browsers have the ability to launch different browser profiles. Each profile can come with a different theme and a different set of website logins (cookies, application state, etc). This can be helpful if you want to segregate browsing behavior. For example, I have different browser profiles set up for my personal email, my &#34;consulting&#34; [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Most browsers have the ability to <a href="https://www.chromium.org/developers/creating-and-using-profiles/">launch different browser profiles</a>.
Each profile can come with a different theme and a different set of website
logins (cookies, application state, etc). This can be helpful if you want to
segregate browsing behavior. For example, I have different browser profiles set
up for my personal email, my &quot;consulting&quot; email, the nonprofit I volunteer with.</p>
<img decoding="async" src="/rawblog/images/browser-tabs/profile-selector.png" alt="Browser dialog that lets you select a profile" />
<p>Along with this, command line tools frequently open URL's for various purposes.
Docusaurus launches http://localhost:3000 so you can view documentation content
in your browser. Lots of different tools open browser profiles to complete
authentication workflows - for example, Tailscale starts a web server on your
local computer, asks you to log in via the browser, and then redirects to the
local web server with a valid token.</p>
<p>When you have multiple profiles, it's frustrating when a URL opens in the
&quot;wrong&quot; profile, because this means it's frequently opening in a window where
you're not logged in. When this happens, web services typically redirect you
to a login page, and remove the information about the URL you were trying to
visit (or store it in a cookie). This means if you try and copy and paste the
URL to the &quot;right&quot; profile, you frequently lose track of the initial URL you
wanted to visit! Very annoying.</p>
<p>Fortunately it turns out <strong>there is a solution to this!</strong> The way most command
line tools open URL's on Macs is by simply invoking <code>open</code> (<code>/usr/bin/open</code>),
followed by the URL.</p>
<pre class="chroma"><code><span class="line"><span class="cl">$ open https://example.com/path/to/page
</span></span></code></pre>
<p>But you can pass additional arguments to the application with <code>--args</code>, and
Chrome/Chromium support arguments that let you select the right profile.</p>
<pre class="chroma"><code><span class="line"><span class="cl">open -na <span class="s2">&#34;Google Chrome&#34;</span> --args --profile-directory<span class="o">=</span><span class="s2">&#34;Profile 4&#34;</span> --new-tab <span class="s2">&#34;https://example.com/path/to/page&#34;</span>
</span></span></code></pre>
<p>That will always open URL's in &quot;Profile 4&quot;, whatever you have that configured
to.</p>
<p>How do you find which profile is which? The simplest way is to open
<code>chrome://version</code> in your URL, and then look at the last bit of &quot;Profile Path&quot;
in the page that opens there - it should be 'Default' or 'Profile N'.</p>
<img decoding="async" src="/rawblog/images/browser-tabs/chrome-find-profile.png" alt="Screenshot of the chrome version page, with the profile directory highlighted" />
<p>On Firefox, you provide the name of the profile from <code>about:profiles</code>, e.g.:</p>
<pre class="chroma"><code><span class="line"><span class="cl">open -n -a &#34;Firefox&#34; --args -no-remote -P &#39;kevin@burke.services&#39; -new-tab &#39;https://example.com&#39;
</span></span></code></pre>
<h2>Example configuration</h2>
<p>If you operate a command line tool (or an application that has access to
<code>/usr/bin/open</code>!), <strong>particularly one that opens URL's for authentication,</strong>
please consider implementing support for opening URL's in specific browser
profiles. Example configuration options might be:</p>
<pre class="chroma"><code><span class="line"><span class="cl"><span class="c"># The name of the application you would like to use to open URL&#39;s. Defaults to</span>
</span></span><span class="line"><span class="cl"><span class="c"># &#34;Google Chrome&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">browser_application</span> <span class="p">=</span> <span class="s2">&#34;Google Chrome&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># The specific browser profile to open URL&#39;s in.</span>
</span></span><span class="line"><span class="cl"><span class="nx">browser_profile</span> <span class="p">=</span> <span class="s2">&#34;Profile 5&#34;</span>
</span></span></code></pre>
<p>And then your CLI tool would implement logic similar to this, in the language of
your choice, instead of just shelling out to <code>open</code>.</p>
<pre class="chroma"><code><span class="line"><span class="cl"><span class="n">cmd</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;open&#39;</span><span class="p">,</span> <span class="n">url</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">browser_application</span> <span class="ow">and</span> <span class="n">browser_profile</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="n">name</span> <span class="ow">in</span> <span class="n">browser_application</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="p">[</span><span class="s1">&#39;chrome&#39;</span><span class="p">,</span> <span class="s1">&#39;chromium&#39;</span><span class="p">,</span> <span class="s1">&#39;brave&#39;</span><span class="p">,</span> <span class="s1">&#39;edge&#39;</span><span class="p">]):</span>
</span></span><span class="line"><span class="cl">        <span class="n">cmd</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;open&#39;</span><span class="p">,</span> <span class="s1">&#39;-na&#39;</span><span class="p">,</span> <span class="n">browser_application</span><span class="p">,</span> <span class="s1">&#39;--args&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="sa">f</span><span class="s1">&#39;--profile-directory=</span><span class="si">{</span><span class="n">browser_profile</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;--new-tab&#39;</span><span class="p">,</span> <span class="n">url</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="s1">&#39;firefox&#39;</span> <span class="ow">in</span> <span class="n">browser_application</span><span class="o">.</span><span class="n">lower</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">cmd</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;open&#39;</span><span class="p">,</span> <span class="s1">&#39;-n&#39;</span><span class="p">,</span> <span class="s1">&#39;-a&#39;</span><span class="p">,</span> <span class="n">browser_application</span><span class="p">,</span> <span class="s1">&#39;--args&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="s1">&#39;-no-remote&#39;</span><span class="p">,</span> <span class="s1">&#39;-P&#39;</span><span class="p">,</span> <span class="n">browser_profile</span><span class="p">,</span> <span class="s1">&#39;-new-tab&#39;</span><span class="p">,</span> <span class="n">url</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">subprocess</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">check</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span></code></pre>
<p>Please consider implementing this change in your CLI tool! It would really help
improve usability for CLI based tools. With sufficient interest, maybe we could
create an RFC for both the arguments browsers should accept, and for environment
variables that can be read by CLI's for opening URL's in a given browser.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://kevin.burke.dev/kevin/open-urls-in-specific-browser-profile/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>github.com/garyburd/redigo has been deleted</title>
		<link>https://kevin.burke.dev/kevin/github-com-garyburd-redigo-has-been-deleted/</link>
					<comments>https://kevin.burke.dev/kevin/github-com-garyburd-redigo-has-been-deleted/#respond</comments>
		
		<dc:creator><![CDATA[kevin]]></dc:creator>
		<pubDate>Sun, 25 May 2025 20:29:42 +0000</pubDate>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Today's World]]></category>
		<guid isPermaLink="false">https://kevin.burke.dev/?p=3874</guid>

					<description><![CDATA[One of the first ever Redis libraries for Go was hosted at github.com/garyburd/redigo. It has been deprecated for some time and has now been finally removed altogether from Github. If you still have a dependency on this project, this means that will be broken now. github.com/gomodule/redigo should be a drop-in replacement for github.com/garyburd/redigo. If you [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>One of the first ever Redis libraries for Go was hosted at
github.com/garyburd/redigo.</p>
<p>It has been deprecated for some time and has now been finally removed altogether
from Github. If you still have a dependency on this project, this means that
will be broken now.</p>
<p><a href="">github.com/gomodule/redigo</a> should be a drop-in replacement for
github.com/garyburd/redigo. If you still depend on the old service, update the
organization name, and you should be good to go.</p>
<p>My hope in writing this is to save time for the next person who is searching for
a replacement for this library. Please pay it forward by taking a tiny bit of
extra time the next time something breaks, and tell people how to fix the
problem.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://kevin.burke.dev/kevin/github-com-garyburd-redigo-has-been-deleted/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Transcript of Oval Office meeting between Volodymyr Zelensky, JD Vance, and Donald Trump &#8211; February 28, 2025</title>
		<link>https://kevin.burke.dev/kevin/transcript-of-oval-office-meeting-between-volodymyr-zelensky-jd-vance-and-donald-trump-february-28-2025/</link>
					<comments>https://kevin.burke.dev/kevin/transcript-of-oval-office-meeting-between-volodymyr-zelensky-jd-vance-and-donald-trump-february-28-2025/#respond</comments>
		
		<dc:creator><![CDATA[kevin]]></dc:creator>
		<pubDate>Fri, 28 Feb 2025 20:31:29 +0000</pubDate>
				<category><![CDATA[News]]></category>
		<guid isPermaLink="false">https://kevin.burke.dev/?p=3870</guid>

					<description><![CDATA[Source: CSPAN Transcript was generated using OpenAI Whisper and then cleaned up using ChatGPT o1 pro. All errors are mine. Volodymyr Zelensky Do you, and now I’m talking with my friends in Poland, and they are worried that you align yourself too much with Putin. What’s your message for them? Donald Trump Well, if I [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Source: <a href="https://x.com/cspan/status/1895540802262126924">CSPAN</a></p>
<p>Transcript was generated using <a href="https://openai.com/index/whisper/">OpenAI
Whisper</a> and then cleaned up using ChatGPT
o1 pro. All errors are mine.</p>
<p><strong>Volodymyr Zelensky</strong><br/>
Do you, and now I’m talking with my friends in Poland, and they are worried that you align yourself too much with Putin.<br/>
What’s your message for them?<br/></p>
<p><strong>Donald Trump</strong><br/>
Well, if I didn’t align myself with both of them, you’d never have a deal.<br/>
You want me to say really terrible things about Putin and then say, “Hi Vladimir, how are we doing on the deal?”<br/>
That doesn’t work that way.<br/>
I’m not aligned with Putin, and I’m not aligned with anybody.<br/>
I’m aligned with the United States of America and for the good of the world.<br/>
I’m aligned with the world, and I want to get this thing over with.<br/>
You see the hatred he’s got for Putin.<br/>
It’s very tough for me to make a deal with that kind of thing.<br/>
He’s got tremendous hatred, and I understand that, but I can tell you the other side isn’t exactly in love with him either.<br/>
So it’s not a question of alignment.<br/>
I’m aligned with the world.<br/>
I want to get the things that—I’m aligned with Europe.<br/>
I want to see if we can get this thing done.<br/>
You want me to be tough? I could be tougher than any human being you’ve ever seen.<br/>
I’d be so tough, but you’re never going to get a deal that way.<br/>
So that’s the way it goes. One more question.<br/></p>
<p><strong>JD Vance</strong><br/>
I want to respond to this.<br/>
For four years in the United States of America, we had a president who stood up at press conferences and talked tough about Vladimir Putin, and then Putin invaded Ukraine and destroyed a significant chunk of the country.<br/>
The path to peace and the path to prosperity is maybe engaging in diplomacy.<br/>
We tried the pathway of Joe Biden of thumping our chest and pretending that the president of the United States’ words mattered more than the president’s actions.<br/>
What makes America a good country is America engaging in diplomacy.<br/>
That’s what President Trump is doing.<br/></p>
<p><strong>Volodymyr Zelensky</strong><br/>
Can I ask you?<br/></p>
<p><strong>JD Vance</strong><br/>
Sure.<br/>
Yeah?<br/>
Yeah.<br/></p>
<p><strong>Volodymyr Zelensky</strong><br/>
He occupied our parts, big parts of Ukraine—parts of the east and Crimea—in 2014.<br/>
For a lot of years, I’m not speaking just about Biden, but during that time it was President Obama, then President Trump, then President Biden, and now President Trump again.<br/>
God bless, now President Trump will stop him.<br/>
But during 2014, nobody stopped him.<br/>
He just occupied and took.<br/>
He killed people.<br/>
You know what the contact line is? 2014, 2015.<br/>
During 2014 till 2022, people have been dying on the contact line, and nobody stopped him.<br/>
We had a lot of conversations with him—my bilateral conversations—and when I was a new president in 2019, I signed with him, Macron, and Merkel a ceasefire.<br/>
All of them told me that he would never go.<br/>
We also signed a gas contract.<br/>
Yes, but after that, he broke the ceasefire.<br/>
He killed our people, and he didn’t exchange prisoners.<br/>
We signed the exchange of prisoners, but he didn’t do it.<br/>
What kind of diplomacy are you speaking about?<br/>
What do you mean?<br/></p>
<p><strong>JD Vance</strong><br/>
I’m talking about the kind of diplomacy that’s going to end the destruction of your country.<br/>
Mr. President, with respect, I think it’s disrespectful for you to come into the Oval Office and try to litigate this in front of the American media.<br/>
Right now, you guys are going around and forcing conscripts to the front lines because you have manpower problems.<br/>
You should be thanking the president for trying to bring an end to this conflict.<br/></p>
<p><strong>Volodymyr Zelensky</strong><br/>
Have you ever been to Ukraine?<br/>
Do you see what problems we have? Then come once.<br/></p>
<p><strong>JD Vance</strong><br/>
I have actually watched and seen the stories, and I know what happens is you bring people on a propaganda tour, Mr. President.<br/>
Do you disagree that you’ve had problems bringing people into your military?<br/>
And do you think that it’s respectful to come to the Oval Office of the United States of America and attack the administration that is trying to prevent the destruction of your country?<br/></p>
<p><strong>Volodymyr Zelensky</strong><br/>
A lot of questions. Let’s start from the beginning.<br/>
First of all, during the war, everybody has problems.<br/>
Even you, but you have a nice ocean and don’t feel it now, but you will feel it in the future.<br/>
You don’t know that. You don’t know that.<br/>
You’re trying to solve a problem—don’t tell us what we’re going to feel.<br/></p>
<p><strong>JD Vance</strong><br/>
I’m not telling you, but you’re in no position to dictate that.<br/>
You’re in no position to dictate what we’re going to feel.<br/></p>
<p><strong>Volodymyr Zelensky</strong><br/>
That’s exactly what you are.<br/></p>
<p><strong>Donald Trump</strong><br/>
We’re going to feel very good and very strong.<br/></p>
<p><strong>Volodymyr Zelensky</strong><br/>
You will feel influenced.<br/></p>
<p><strong>Donald Trump</strong><br/>
You’re right now not in a very good position.<br/>
You’ve allowed yourself to be in a very bad position from the very beginning of the war.<br/>
You don’t have the cards right now. With us, you don’t have the cards.<br/>
You’re gambling with the lives of millions of people.<br/>
You’re gambling with World War III.<br/>
You’re gambling with World War III, and what you’re doing is very disrespectful to this country.<br/>
This country has backed you far more than a lot of people said they should have.<br/>
Have you said thank you once in this entire meeting?<br/></p>
<p><strong>JD Vance</strong><br/>
You went to Pennsylvania and campaigned for the opposition in October.<br/>
Offer some words of appreciation for the United States of America and the president who’s trying to save your country.<br/>
Please.<br/></p>
<p><strong>Volodymyr Zelensky</strong><br/>
You think that if you speak very loudly about the war…<br/>
My country is in big trouble, but we are staying strong from the beginning of the war.<br/>
We’ve been alone, and we are thankful.<br/>
I said thanks in this cabinet.<br/></p>
<p><strong>Donald Trump</strong><br/>
You haven’t been alone.<br/>
We gave you, through your stupid president, 350 billion dollars.<br/>
We gave you military equipment, and your men are brave, but they had to use our military equipment.<br/>
If you didn’t have our military equipment, this war would have been over in two weeks—maybe three days, maybe less.<br/>
It’s going to be a very hard thing to do business like this.<br/>
Just say thank you.<br/></p>
<p><strong>Volodymyr Zelensky</strong><br/>
I said it at all times, except that there are disagreements.<br/></p>
<p><strong>JD Vance</strong><br/>
Except that there are disagreements, and let’s go litigate those disagreements rather than trying to fight it out in the American media when you’re wrong.<br/>
We know that you’re wrong.<br/></p>
<p><strong>Donald Trump</strong><br/>
But you see, I think it’s good for the American people to see what’s going on.<br/>
I think it’s very important.<br/>
That’s why I kept this going so long.<br/>
You have to be thankful.<br/>
You don’t have the cards.<br/>
You’re buried there. You have people who died.<br/>
You’re running low on soldiers. You’re running low on soldiers.<br/>
Then you tell us, “I don’t want a ceasefire.”<br/>
Look, if you could get a ceasefire right now, I’d tell you to take it so the bullets would stop flying and your men would stop getting killed.<br/></p>
<p><strong>Volodymyr Zelensky</strong><br/>
Of course, we want to stop the war.<br/>
But I said to you, I want a ceasefire with guarantees, because you’ll get a ceasefire faster than an agreement.<br/>
Ask our people about a ceasefire—what they think.<br/>
It doesn’t matter to you what they think.<br/></p>
<p><strong>Donald Trump</strong><br/>
That wasn’t with me. It wasn’t with you.<br/>
That was with a guy named Biden who was not a smart person.<br/>
That was with Obama, who gave you sheets, while I gave you Javelins.<br/>
I gave you Javelins to take out all those tanks. Obama gave you sheets.<br/>
“Obama gave sheets, and Trump gave Javelins.”<br/>
You’ve got to be more thankful, because let me tell you, you don’t have the cards.<br/>
With us, you have the cards, but without us, you don’t have any cards.<br/></p>
<p><strong>Volodymyr Zelensky</strong><br/>
One more question to the Vice President…<br/></p>
<p><strong>Donald Trump</strong><br/>
I’m sorry. It’s going to be a tough deal to make because the attitudes have to change.<br/></p>
<p><strong>Volodymyr Zelensky</strong><br/>
What if Russia breaks this fire?<br/>
What if Russia breaks these talks?<br/>
What if the bomb drops on your head right now?<br/></p>
<p><strong>Donald Trump</strong><br/>
Okay, what if they broke it? I don’t know.<br/>
They broke it with Biden because Biden didn’t respect him.<br/>
They didn’t respect Obama. They respect me.<br/>
Putin went through a hell of a lot with me.<br/>
He went through a phony witch hunt where they used him in “Russia, Russia, Russia.”<br/>
You ever hear of that deal?<br/>
That was a phony Hunter Biden, Joe Biden scam—Hillary Clinton, shifty Adam Schiff.<br/>
It was a Democrat scam, and he had to go through that.<br/>
We didn’t end up in a war. He was accused of all that stuff. He had nothing to do with it.<br/>
It came out of Hunter Biden’s bathroom, out of Hunter Biden’s bedroom. It was disgusting.<br/>
Then they said, “Oh, the laptop from hell was made by Russia,” the 51 agents.<br/>
The whole thing was a scam.<br/>
Maybe he broke deals with Obama and Bush, and maybe he broke them with Biden—he did, maybe. Maybe he didn’t. I don’t know.<br/>
He’s not breaking with me. He wants to make a deal. I don’t know if you can make a deal.<br/>
The problem is I’ve empowered you to be a tough guy, and I don’t think you’d be a tough guy without the United States.<br/>
Your people are very brave, but you’re either going to make a deal or we’re out.<br/>
If we’re out, you’ll fight it out. I don’t think it’s going to be pretty, but you’ll fight it out.<br/>
You don’t have the cards. Once we sign that deal, you’re in a much better position.<br/>
But you’re not acting at all thankful, and that’s not a nice thing. I’ll be honest—that’s not a nice thing.<br/></p>
<p><strong>JD Vance</strong><br/>
All right, I think we’ve seen enough.<br/>
What do you think? This is going to be great television.<br/>
We’ll see what we can do about putting it together.<br/>
Thanks, sir.<br/></p>
<p><strong>Volodymyr Zelensky</strong><br/>
Thanks, sir.<br/></p>
]]></content:encoded>
					
					<wfw:commentRss>https://kevin.burke.dev/kevin/transcript-of-oval-office-meeting-between-volodymyr-zelensky-jd-vance-and-donald-trump-february-28-2025/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Cities Can Cost Effectively Start Their Own Utilities Now</title>
		<link>https://kevin.burke.dev/kevin/norcal-cities-new-utility/</link>
					<comments>https://kevin.burke.dev/kevin/norcal-cities-new-utility/#comments</comments>
		
		<dc:creator><![CDATA[kevin]]></dc:creator>
		<pubDate>Fri, 07 Feb 2025 17:47:56 +0000</pubDate>
				<category><![CDATA[Policy]]></category>
		<guid isPermaLink="false">https://kevin.burke.dev/?p=3866</guid>

					<description><![CDATA[Most PG&#38;E ratepayers don't understand how much higher the rates they pay are than what it actually costs PG&#38;E to generate and transmit the electricity to their house. When I looked into this recently I was shocked. The average PG&#38;E electricity charge now starts at 40 cents per kilowatt hour and goes up from there. [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Most PG&amp;E ratepayers don't understand how much higher the rates they
pay are than what it actually costs PG&amp;E to generate and transmit the
electricity to their house. When I looked into this recently I was shocked.
The average PG&amp;E electricity charge now <a href="https://www.pge.com/assets/pge/docs/account/rate-plans/residential-electric-rate-plan-pricing.pdf"><em>starts at</em> 40 cents per kilowatt
hour and goes up from there</a>. Silicon Valley Power, Santa Clara's
utility company, is getting power to customers for <a href="https://www.siliconvalleypower.com/residents/rates-and-fees">17 cents per kilowatt
hour</a>. Sacramento's utility company charges <a href="https://www.smud.org/Rate-Information/Residential-rates/Fixed-Rate">about the
same</a>.</p>
<p>PG&amp;E's rates are high enough that, even with the massive headache and expense
involved, it's feasible for cities to create their own utility and undercut
PG&amp;E's rates. When the savings per household are around $800-$1200 per
year, though, they should take it seriously.</p>
<p>Here are the basic components of how much it costs to get electricity to your
house.</p>
<ul>
<li>
<p><strong>Generation:</strong> The cost to actually generate the electricity in a power plant
or utility-scale solar farm. This varies by time of day but typically costs
about 4 cents per kilowatt hour; you can see the current wholesale rate on <a href="https://www.caiso.com/todays-outlook/prices">the
CAISO website</a>.</p>
</li>
<li>
<p><strong>Transmission:</strong> How much it costs to move the power from the power source to
a local substation/transformer, over large transmission lines. PG&amp;E breaks this
out in its detailed rate chart at about 4 cents per kilowatt hour.</p>
</li>
<li>
<p><strong>Distribution:</strong> How much to get the power from your local substation to your
house over local power lines. In PG&amp;E's rate chart, they charge <em>20 cents per
kilowatt hour</em> for this. That just does not match up with how much it actually
costs them to transmit power over local lines and keep the lines maintained.</p>
</li>
<li>
<p><strong>Everything else:</strong> Operations, maintenance, profit. This is where PG&amp;E is
actually seeing large expenses, because their coverage area is massive, it
costs a lot of money to deliver power to rural customers, and they are also
undertaking a massive project to underground utility lines in fire-prone areas.</p>
</li>
</ul>
<p>The high price and design of the electricity system have a number of bad
effects:</p>
<ul>
<li>
<p>People really hate inflation. When utility bills spike, it makes people
unhappy and also fuels the (not incorrect) perception that California is poorly
governed.</p>
</li>
<li>
<p>Lower income people spend a higher percentage of their income on electricity,
so higher utility bills disproportionately hurt them.</p>
</li>
<li>
<p>The net effect of charging higher rates to everyone to pay for undergrounding
is that people who live in urban areas are paying more money to subsidize
energy transmission for people who live in $2 million houses in places like the
Berkeley and Orinda hills. This makes no sense.</p>
</li>
<li>
<p>Higher rates for electricity make electricity less competitive vs. gasoline
when people are considering a car purchase. It makes electricity less
competitive vs. natural gas for heating a house, heating water, or choosing a
laundry machine. As gas is warming the planet and electricity is substantially
easier to generate in abundance from renewable sources, it's just bad policy to
have high electricity rates.</p>
</li>
</ul>
<p>Let's walk through what this might look like for a particular city to undercut
PG&amp;E's rates. I will pick Walnut Creek because it's a reasonably big city with
a good mix of detached homes and multifamily. Walnut Creek also has experience
with public ownership of amenities - the City operates a golf course and a
downtown parking garage with ground floor retail.</p>
<p>There are a number of particular problems with applying PG&amp;E's rates to Walnut
Creek:</p>
<ul>
<li>
<p>Walnut Creek is an urban area with a compact footprint that has little acreage
in a high severity wildfire zone. It has two transmission lines as well as a
local transformer grid along Ygnacio Valley Road. It is very cheap to transmit
power from power plants to Walnut Creek, and from transmission lines to every
house in Walnut Creek.</p>
</li>
<li>
<p>Walnut Creek has an above average number of apartments. Apartments do not
have as much space for rooftop solar, and landlords don't have an incentive to
provide rooftop solar because they typically pass through utility costs. This
means NEM1 and NEM2 subsidies — <a href="https://lao.ca.gov/reports/2025/4950/Residential-Electricity-Rates-010725.pdf#page=8">12% of the average non-solar bill</a>
— disproportionately hurt Walnut Creek renters.</p>
</li>
<li>
<p>Local businesses have disproportionately high energy costs. Safeway and Whole
Foods need to keep a row of refrigerators and freezers running 24/7. When they
pay PG&amp;E's rates to do that, those high energy costs are passed through as
higher food prices.</p>
</li>
</ul>
<p>We are going to adapt <a href="https://cityofpaloalto.primegov.com/api/compilemeetingattachmenthistory/historyattachment/?historyId=bc50598a-8880-41ba-bed3-39b6fcd00890">Palo Alto's income statement, found on page 50 of
their annual report</a>, to Walnut Creek's situation. For each
category, I am going to try to shade the numbers assuming we can't be as
efficient as Palo Alto.</p>
<h3>Usage, Revenue, Expenses</h3>
<p>Palo Alto's total electric consumption was 830 gigawatt hours in 2024 - 19% of
this usage was residential, and 81% was businesses and industry uses. Applying
some adjustments for Walnut Creek - our population is bigger, it's a bit hotter
here, and energy use has increased - let's say Walnut Creek uses about 1150
gigawatt hours per year.</p>
<p>Palo Alto earned $172 million in revenue for 830 gigawatt hours, which is about
20 cents per kilowatt hour.</p>
<h3>Expenses</h3>
<p>Here's where Palo Alto's utility company spends money:</p>
<h4>Acquisition of the network and financing cost</h4>
<p>The first thing you need to do is buy out PG&amp;E's distribution network - all of
the power poles and local equipment that sits between the transmission lines and
people's houses. San Francisco proposed buying this for $2.5 billion in 2019;
PG&amp;E rejected this offer for being too low. Adjusted for inflation and Walnut
Creek's population, this is about $230 million, let's round up and say $350
million. Let's also assume it costs $50 million in startup costs and one time
expenses to hire utility staff, buy equipment, marketing expense.</p>
<p>Cities with an AA credit rating can issue a 30 year loan at about 4% interest.
Borrowing $400 million would cost about $23 million per year in interest and
principal payments.</p>
<p>$23 million per year of financing cost spread across 1150 gigawatt hours is
only about 2 cents per kilowatt hour.</p>
<h4>Generation and distribution</h4>
<p>Palo Alto spent $114 million buying energy in 2024, about 14 cents per kilowatt
hour. Let's assume Walnut Creek can get power for about 17 cents per kWh.</p>
<h4>Operations</h4>
<p>This covers customer service, financial management, billing, engineering work
for maintenance (tree trimming etc), and resource management. Palo Alto spent
$65 million on these expenses in 2023. Let's assume Walnut Creek's costs are
much higher at $90 million per year. This is about 8 cents per kilowatt hour.</p>
<h4>Capital Improvements</h4>
<p>Another $25 million per year is allocated for grid modernization,
undergrounding, and reliability work. Let's assume this is $35 million per year
for Walnut Creek, which would be about 3 cents per kilowatt hour.</p>
<h3>Total</h3>
<p>Adding this up, we get 30 cents per kilowatt hour, which is ten cents lower than
PG&amp;E's base rate and about 15 cents lower than PG&amp;E's blended rate. At 1150
gigawatt hours, <em>this would save Walnut Creek residential ratepayers about $23
million per year in total, about $800 per ratepayer, and Walnut Creek businesses
about $92 million per year.</em> That is a <em>huge</em> amount of money that could go
toward much more productive uses - paying higher salaries, lowering prices for
goods, spending more at local businesses.</p>
<p>Most elected officials would jump at the chance to mail every household a $800
check every year. The next best thing is to put $800 back in their pocket.</p>
<h3>Other Benefits for Walnut Creek</h3>
<p>There are huge ancilliary benefits for Walnut Creek to running its own utility
network.</p>
<ul>
<li><strong>Easier and cheaper infrastructure upgrades:</strong> Public safety is a large
concern in Walnut Creek. Things like installing new streetlights can take a
substantial amount of time and coordination. PG&amp;E <a href="https://www.berkeleyside.org/2023/04/20/berkeley-san-pablo-avenue-pedestrian-signal-caltrans">took over nine months just to
turn on the power for a crosswalk in Berkeley</a>. With local control of
the utility, Walnut Creek can deliver investments in things like streetlights,
<a href="https://www.kqed.org/news/11943157/how-pge-adds-months-long-delays-costs-to-new-housing">new housing</a> or HAWK signals faster and cheaper.</li>
</ul>
<ul>
<li><strong>Green infrastructure investments:</strong> Walnut Creek has made sustainability a
key priority. Palo Alto owns a share in a hydroelectric dam, and Santa Clara
owns a share in a geothermal plant. At a time when there are exciting new
technologies that have the potential to reduce greenhouse gas emissions and
deliver clean, cheap energy to residents - things like Fervo Energy that <a href="https://www.washingtonpost.com/climate-environment/interactive/2024/fracking-geothermal-energy-plant-technology/">use
the tech behind fracking to deliver geothermal power</a> - Walnut
Creek can use its very low cost of capital to finance these investments. <strong>This
is something PG&amp;E cannot do as effectively, because as a public utility with
massive amounts of debt and wildfire liability, their borrowing cost is much
higher.</strong> Public ownership would enable transformative green energy investments
with a low borrowing cost.</li>
</ul>
<ul>
<li>
<p><strong>Encouraging the green transition:</strong> A 25% reduction in the cost of
electricity relative to natural gas would make electric upgrades like heat pump
water heaters or electric cars much more financially prudent investments.</p>
</li>
<li>
<p><strong>Fiscal stabilizer:</strong> Like every city in California, Walnut Creek has boom
and bust cycles. Utilities have much more stable revenues than cities. Walnut
Creek could borrow from its utility in recessions, and loan money during booms.</p>
</li>
<li>
<p><strong>Encourage incorporation:</strong> Walnut Creek has a number of unincorporated
pockets (<a href="https://en.wikipedia.org/wiki/San_Miguel,_Contra_Costa_County,_California">San Miguel CDP</a>, Shell Ridge CDP) that
administratively make little sense - they are served by different police,
they have different tax rules. If these homes could save $800 per year on
their utility bill by joining Walnut Creek, this may provide an incentive to
incorporate, which would ultimately lead to better governance.</p>
</li>
</ul>
<ul>
<li>
<p><strong>The best alternatives are good:</strong> PG&amp;E is terrified of cities leaving its
service network. <a href="https://www.siliconvalley.com/2024/12/03/pge-san-jose-public-utility-energy-development/">Look at the concessions they are offering to South San Jose
to try to prevent them from starting their own utility</a>.</p>
<p>Even if Walnut Creek doesn't ultimately pursue its own utility, just
investigating the possibility may lead PG&amp;E to <em>offer concessions such as
undergrounding the transmission line over downtown.</em> Because you can't
build under a transmission line, this makes a 100 foot wide strip of very
valuable land undevelopable. St. Paul's would love to redevelop its parking
lot under the transmission line for affordable housing, but can only develop
tiny corners of the lot with the transmission line overhead. Undergrounding
the line would deliver huge benefits to Walnut Creek.</p>
<p><img decoding="async" src="/rawblog/images/walnut-creek-transmission.png" alt="Transmission
line over downtown Walnut Creek" /></p>
</li>
<li>
<p><strong>Lowering the cost of urban living in safe places:</strong> PG&amp;E's current rate
structure has urban rate payers subsidize rural rate payers and people who live
in wildfire zones in e.g. the Orinda Hills, who need substantial investment in
order to receive power without sparking wildfires. This is bad policy - instead
of subsidizing fire zones, it should be cheap to live in safe places and more
expensive to live in dangerous places. Lower cost of electricity would reverse
these trends.</p>
</li>
</ul>
<p>California is kneecapping its own climate transition with high electricity
prices. The resulting inflation hurts our state's ability to retain a high
class, diverse workforce. Perversely, it also serves as a subsidy to wildfire
zones at the expense of infill areas. It's time to reverse those trends and
deliver lower energy prices in places we want more Californians to live.</p>
<p><strong>What should I do if I want this to happen?</strong> Cities around the region are
doing &quot;priority setting&quot; exercises for 2025. Contact your Mayor or City
Council and ask them to explore the possibility of creating their own utility,
potentially partnering with other cities. I would probably select cities that do
not have large fire zones (ie, not Orinda or Moraga).</p>
]]></content:encoded>
					
					<wfw:commentRss>https://kevin.burke.dev/kevin/norcal-cities-new-utility/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title>How to Ventilate Your House</title>
		<link>https://kevin.burke.dev/kevin/how-to-ventilate-your-house/</link>
					<comments>https://kevin.burke.dev/kevin/how-to-ventilate-your-house/#respond</comments>
		
		<dc:creator><![CDATA[kevin]]></dc:creator>
		<pubDate>Tue, 23 Aug 2022 05:26:45 +0000</pubDate>
				<category><![CDATA[Education]]></category>
		<category><![CDATA[Pollution]]></category>
		<guid isPermaLink="false">https://kevin.burke.dev/?p=3856</guid>

					<description><![CDATA[Every day we learn more about the importance of good air quality. Here are some tips to help you improve air quality inside your house. How to Measure First, you are going to want to be able to measure air quality in your house. There are a few different things you want to measure: PM2.5 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Every day we learn more about the importance of good air quality. Here are some
tips to help you improve air quality inside your house.</p>
<h3>How to Measure</h3>
<p>First, you are going to want to be able to measure air quality in your house.
There are a few different things you want to measure:</p>
<ul>
<li>
<p>PM2.5 measures the amount of particles that are 2.5 micrometers or smaller.
Compared with larger particles, <a href="https://www.epa.gov/pm-pollution/particulate-matter-pm-basics#effects">PM 2.5 does the most damage</a> because
they are so small that they can get embedded far into your lungs, which is why
they are measured and tracked. You want to keep this value as low as possible.</p>
</li>
<li>
<p>AQI is an aggregate index that measures PM 2.5, PM 10 (10 micrometer
particles), carbon monoxide, sulfur dioxide, and nitrogen dioxide in the air.
The <a href="https://www.airnow.gov/sites/default/files/2020-05/aqi-technical-assistance-document-sept2018.pdf#page=13">formula for calculating it is complicated</a> but basically takes the
largest value out of those. You want to keep this value below 50.</p>
</li>
<li>
<p>Carbon dioxide builds up in a stuffy room with no ventilation and <a href="https://www.theguardian.com/environment/2019/jul/08/indoor-carbon-dioxide-levels-could-be-a-health-hazard-scientists-warn">can affect
cognitive performance, make you sleepy, and have other bad side effects</a>.
400 is the ambient air; a value over 1000 is bad.</p>
</li>
</ul>
<p>To measure PM2.5 and AQI, I like the <a href="https://www.amazon.com/Temtop-M10-Professional-Electrochemical-Rechargeable/dp/B07DHXQXGK?th=1&amp;linkCode=ll1&amp;tag=kbure-20&amp;linkId=ba57f816e1bf77e7e2fc9a0f0a29ebc3&amp;language=en_US&amp;ref_=as_li_ss_tl">Temtop M10</a>, though it's very easy
to buy a more expensive piece of gear that you can connect to Wifi, or smart
devices.</p>
<p>To measure CO2, I like <a href="https://www.amazon.com/Aranet4-Home-Temperature-Ink-Configuration/dp/B07YY7BH2W?&amp;linkCode=ll1&amp;tag=kbure-20&amp;linkId=96876f59c4aa2196a280e771a33f90ca&amp;language=en_US&amp;ref_=as_li_ss_tl">the Aranet4</a>, which can run forever on a single
battery.</p>
<p>If you don't want to buy them, try to borrow one from a friend, or see if your
library will buy them and loan them out; you'll get a sense for the ranges of
air quality in your house after a few days.</p>
<h3>When it's nice outside</h3>
<p>The easiest case is when the weather is nice outside, and there's no fires or
smoky air, and you don't live near a freeway. Open your windows! Bringing in
outside air is the only way to lower CO2 counts (you can't just run an air
purifier).</p>
<p>The best way to ventilate <em>quickly</em> is to open windows on both sides of your
house, called &quot;cross ventilation,&quot; so a breeze can flow from one end to the
other. A good sign that this is working is that doors between the two windows
are slamming shut.</p>
<h3>If it's not nice outside</h3>
<p>Sometimes you can't or shouldn't keep your windows open - it's too hot or too
cold, or the air quality outside is too bad. If you live within 1000 feet of a
freeway, you are likely <a href="https://www.latimes.com/local/california/la-me-freeway-pollution-what-you-can-do-20171230-htmlstory.html">breathing in polluted air almost all the time</a>
you are outside or have your windows open, which has been linked to lower birth
weight, worse cognitive performance, higher rates of respiratory illness.</p>
<p>You are going to want to pump in outside air anyway. If it's hot or cold, you
can use your AC, or your heat, which will take care of this for you, by swapping
in new air and swapping out old air.</p>
<p>If you have <em>either</em> of these and can't or don't want to change the temperature,
they should have a &quot;fan&quot; mode, which does not apply heat but will just bring in
new air. You should be running this mode almost all of the time you have your
windows closed.</p>
<p>Your heat or your AC will have a filter between the outside air and the fan.
Mine is 16x25x1, and looks like this:</p>
<p><img decoding="async" src="/rawblog/images/heater-fan-filter.jpeg" alt="Filter for heating
system" /></p>
<p><strong>You need to buy a good filter.</strong> Filters are rated on the MERV system, which
sorts by how much particles they are supposed to catch. Your average filter will
be about MERV 5. You want to buy a MERV 13 level filter, which is only a little
bit more expensive but is going to be vastly better at stopping particulates.</p>
<p>Plan to replace your filter every 2-3 months, more if you run the fan more
often, less if you run the fan less often.</p>
<p>You can supplement outside air with an air purifier like <a href="https://www.amazon.com/Coway-AP-1512HH-Mighty-Purifier-True/dp/B00BTKAPUU?&amp;linkCode=ll1&amp;tag=kbure-20&amp;linkId=9e50138f000b3df865488ad8edbca99e&amp;language=en_US&amp;ref_=as_li_ss_tl">the Coway
AP1512-HH</a>. Each air purifier comes with a rating for how big of a room
it is designed to clear; this one is for 360 square feet. When it ships to you,
the filters are already in the purifier, but covered in plastic. Be sure to
remove the plastic or the filters won't work.</p>
<p>I keep my air purifiers (we have four) on the lowest level all of the time.
Note that these will be ineffective at lowering carbon dioxide levels; you need
outside air for that. Plan to change the odor filter every 6 months and the HEPA
filter once a year, more often if you live in a particularly polluted area.</p>
<h3>Sources of bad air inside your house</h3>
<p>The primary source of bad air inside your house is your stovetop. When we cook,
the AQI inside our house spikes up above 300, which is worse than in Beijing.
If the fan above your stove vents to the outside, you almost always want to run
this at the highest level, in addition to opening your windows and doors, and
running your house fan, in order to bring the air quality back to a decent level
as quickly as possible.</p>
<p>Other sources of bad air include cleaning products and using the toilet.
Understand if bathroom fans vent to the outside (they should, if your house is
up to code) and run them. Open windows when you clean.</p>
<p>Your stove, your oven, and your shower will also make your kitchen hot, which
can be uncomfortable if you are trying to keep the windows closed. On hot days
consider using the BBQ instead, if you have one.</p>
<h2>That's it</h2>
<p>Modern homes in particular are designed not to leak any air if you don't want
to. While this is good news for efficiently heating or cooling your house, it
is bad news for air quality inside. Measure air quality inside your house and
understand which tools are best for bringing in better air.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://kevin.burke.dev/kevin/how-to-ventilate-your-house/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How much does a San Francisco Chronicle subscription cost?</title>
		<link>https://kevin.burke.dev/kevin/san-francisco-chronicle-pricing/</link>
					<comments>https://kevin.burke.dev/kevin/san-francisco-chronicle-pricing/#comments</comments>
		
		<dc:creator><![CDATA[kevin]]></dc:creator>
		<pubDate>Mon, 06 Jun 2022 04:03:03 +0000</pubDate>
				<category><![CDATA[News]]></category>
		<guid isPermaLink="false">https://kevin.burke.dev/?p=3848</guid>

					<description><![CDATA[The San Francisco Chronicle charges for subscriptions. How much does a subscription cost? This is an impossible question to answer, even for current subscribers. The Chronicle advertises several different prices for new subscribers. The only public information the Chronicle shares about its permanent subscription rates raises more questions than answers. No one at the Chronicle [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>The San Francisco Chronicle charges for subscriptions. How much does a
subscription cost? This is an impossible question to answer, even for current
subscribers. The Chronicle advertises several different prices for new
subscribers.</p>
<p>The <a href="https://www.sfchronicle.com/faq/#faq01">only public information the Chronicle shares about its permanent
subscription rates</a> raises more questions than answers.</p>
<p><img decoding="async" src="/rawblog/images/chronicle-faq-pricing.png" alt="Chronicle pricing
page" /></p>
<p>No one at the Chronicle has ever read through this section.</p>
<ul>
<li>
<p>The digital-only rate is $34.50 per week, which means that it is about twice
the price of <em>getting the Chronicle delivered to your house every single day.</em></p>
</li>
<li>
<p>If you get the Chronicle delivered, you can get a free digital subscription so
you if you want a digital subscription you should opt for delivery.</p>
</li>
<li>
<p>The &quot;home delivery&quot; prices are listed twice and you have to look particularly
close to figure out that one of them includes a digital subscription.</p>
</li>
<li>
<p><em>No one is paying, or going to pay, $34 a week for digital access to the
Chronicle!??</em></p>
</li>
</ul>
<p>I only started looking into this because after three years, the Chronicle
decided to more than double the price of my subscription, from $149 a year
to $358 per year. The <a href="https://www.sfchronicle.com/terms_of_use/">terms of service say that they are supposed to notify
me</a> when they change the price (&quot;We will notify you of any
changes if the regular fee for your subscription changes from what was stated at
the time of your initial order.&quot;). They sent no notification, the first I found
out was on my credit card bill.</p>
<p><img decoding="async" src="/rawblog/images/chronicle-yearly-bill.png" alt="How much I paid for
the Chronicle" /></p>
<p>I then tried to find any public information the Chronicle had posted about its
subscription pricing, whether they had announced recently that they were raising
their prices to invest in the newsroom, anything.</p>
<p>The only thing I found was that FAQ page that says a digital subscription costs
$34 a week. This is the only public information anywhere that the Chronicle
offers about its rates. There is no notion of a &quot;subscription plan.&quot; I couldn't
really believe this, so I called the Chronicle customer service line. They
confirmed that the Chronicle could decide at any time to charge you $34 per
week, or about $1800 per year, for a Chronicle subscription, and anyone who got
a lower price than that was just lucky to have it.</p>
<p>By contrast the New York Times clearly shows the introductory price and the
standard price.</p>
<img decoding="async" src="/rawblog/images/nytimes-pricing.png" />
<p>The Washington Post also clearly explains how much you are going to pay.</p>
<img decoding="async" src="/rawblog/images/wapo-pricing.png" />
<p>By contrast someone at the Chronicle must have looked at my 3 year old
subscription and decided I should pay more and charge me for that, without any
notification, or justification, or even explanation that &quot;this is the rate most
people are paying.&quot;</p>
<p>Anyway, if you are a Chronicle subscriber you should be aware that:</p>
<ul>
<li>
<p>The Chronicle can and will change the price of your subscription at any time</p>
</li>
<li>
<p>According to the Chronicle, everyone who buys a subscription is getting
a discount from the $1800/year rate, and should feel lucky.</p>
</li>
<li>
<p>The Chronicle will not tell you why you're paying the rate you're paying.</p>
</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://kevin.burke.dev/kevin/san-francisco-chronicle-pricing/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Sour Punch Strawberry changed their formula</title>
		<link>https://kevin.burke.dev/kevin/sour-punch-strawberry-changed-their-formula/</link>
					<comments>https://kevin.burke.dev/kevin/sour-punch-strawberry-changed-their-formula/#respond</comments>
		
		<dc:creator><![CDATA[kevin]]></dc:creator>
		<pubDate>Mon, 16 May 2022 04:24:09 +0000</pubDate>
				<category><![CDATA[Today's World]]></category>
		<guid isPermaLink="false">https://kevin.burke.dev/?p=3845</guid>

					<description><![CDATA[I have a sweet tooth and one candy I really enjoy is Strawberry Sour Punch. Unfortunately after twenty years of having the same formula it seems that the company has decided to change the formula for Sour Punch. The new candy has more of a cherry flavor and isn't as sour. It does not taste [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I have a sweet tooth and one candy I really enjoy is Strawberry Sour Punch.</p>
<p>Unfortunately after twenty years of having the same formula it seems that the
company has decided to change the formula for Sour Punch. The new candy has more
of a cherry flavor and isn't as sour. It does not taste good at all.</p>
<p>The company confirmed that the formula changed in a DM:</p>
<blockquote>
<p>Due to the global supply chain crisis, we’ve had to temporarily tweak our
formula. We know the current product isn’t what you’ve come to expect from
SOUR PUNCH, which is why we are doing everything we can to return it to its
original deliciousness so that you can enjoy the candy you know and love.</p>
</blockquote>
<p><a href="https://old.reddit.com/r/candy/comments/tpbph5/did_sour_punch_change_its_formula_for_strawberry/">People on Reddit have confirmed the new flavor is hitting shelves</a>.
<a href="https://smile.amazon.com/Sour-Punch-Bites-Strawberry-Flavor/dp/B000UHCC04">Recent reviews on Amazon</a> are not pretty.</p>
<img decoding="async" src="/rawblog/images/sour-punch-reviews.png" />
<p>Have you had a bad batch of Sour Punch, or a batch with a different
flavor? Leave a note in the comments, or <a href="mailto:sourpunch@amerlic.com">contact the American Licorice
Company</a> and ask them to bring the old flavor
back.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://kevin.burke.dev/kevin/sour-punch-strawberry-changed-their-formula/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Get Urgent Care from One Medical</title>
		<link>https://kevin.burke.dev/kevin/one-medical-urgent-care/</link>
					<comments>https://kevin.burke.dev/kevin/one-medical-urgent-care/#respond</comments>
		
		<dc:creator><![CDATA[kevin]]></dc:creator>
		<pubDate>Mon, 02 May 2022 20:25:43 +0000</pubDate>
				<category><![CDATA[Today's World]]></category>
		<guid isPermaLink="false">https://kevin.burke.dev/?p=3841</guid>

					<description><![CDATA[On Sunday morning at 1am I had a lot of stomach pain. I am a One Medical customer, had heard that they offered urgent care, and so I searched on their website for information. Unfortunately their urgent care option does not show up in the list of care options on their website: I concluded that [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>On Sunday morning at 1am I had a lot of stomach pain. I am a One Medical
customer, had heard that they offered urgent care, and so I searched on their
website for information.</p>
<p>Unfortunately their urgent care option does not show up in the list of care
options on their website:</p>
<img decoding="async" src="/rawblog/images/2022-05-one-medical.png" />
<p>I concluded that their urgent care offering didn't actually exist and paid for
a competing provider's product, so I could talk to a doctor at 1am.</p>
<p>This morning I was informed that their urgent care option does exist, but <em>only
in their mobile apps.</em> So you have to go into the iOS app and then you will see
&quot;Video Call Now&quot; as an option.</p>
<p>This is a baffling design choice to me. I don't understand why, as a company,
you'd only put features in one of the several ways customers can use your
product. Or at the very least you could put &quot;Video Call&quot; in the list of options
on the website, and then have a popup that says &quot;open the app.&quot; But they didn't
do that.</p>
<p>So if you are looking to get urgent care or use any One Medical feature try the
app.</p>
<p>If this post was useful please <a href="https://twitter.com/onemedical">send the One Medical twitter
account</a> a DM saying so so they can update their
product.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://kevin.burke.dev/kevin/one-medical-urgent-care/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
