<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"> 
<id>https://sive.rs/tech.xml</id> 
<title>Sivers tech posts</title> 
<subtitle>articles for programmers, techies, coders, sysadmins, or whatever you call us</subtitle> 
<updated>2024-08-31T04:00:00Z</updated>
<link rel="self" type="application/atom+xml" href="https://sive.rs/tech.xml"/>
<link rel="alternate" type="text/html" href="https://sive.rs/tech"/> 
<author><name>Derek Sivers</name><uri>https://sive.rs/</uri></author>
<entry>
	<id>https://sive.rs/macx</id>
	<title>How to sync Mac and Linux /home</title> 
	<published>2024-08-31T04:00:00Z</published>
	<updated>2024-08-31T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/macx"/> 
	<summary type="text">the problem:</summary> 
	<content type="html">&lt;h3&gt;
	the problem:
&lt;/h3&gt;
&lt;p&gt;
	My main computer is a Linux/BSD desktop, but I also use a Mac laptop for recording and travel.
	This created a problem keeping them in sync.
&lt;/p&gt;&lt;p&gt;
	I don’t like that the MacOS user’s home has directories like Movies and Pictures that we can’t delete, or that ~/Library is filled with Mac-only crap I don’t want on my other computer.
	Also, why did Apple make it /Users/me and not &lt;strong&gt;/home/&lt;/strong&gt;me?
&lt;/p&gt;

&lt;h3&gt;
	the solution: synthetic.conf
&lt;/h3&gt;
&lt;p&gt;
	We can use the fact that Mac has no &lt;strong&gt;/home/&lt;/strong&gt; to our advantage.
	In your terminal, type &lt;a href=&quot;https://keith.github.io/xcode-man-pages/synthetic.conf.5.html&quot;&gt;man synthetic.conf&lt;/a&gt; to read about it.
&lt;/p&gt;&lt;p&gt;
	&lt;strong&gt;Assuming username “me” here&lt;/strong&gt;, (meaning: &lt;strong&gt;replace “me”, below, with your actual username&lt;/strong&gt;) I type, in the terminal:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
sudo vi /etc/synthetic.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
	Then I add this one line, which has to be separated with a [tab]:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
home        Users/me
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
	Save, verify, then reboot.
&lt;/p&gt;

&lt;h3&gt;
	/home/me = /Users/me/me
&lt;/h3&gt;
&lt;p&gt;
	Make a new directory ~/me which will now be /home/me
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
mkdir ~/me
touch /home/me/hello
ls ~/me
ls /home/me/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
	Verify it’s working, that whatever you put into /Users/me/me/ is also now in /home/me/ and vice-versa.
	Then…
&lt;/p&gt;

&lt;h3&gt;
	rsync
&lt;/h3&gt;
&lt;p&gt;
	Of course you have to be able to SSH into your other computer, so if not, go figure that out first.
	I’ll assume that “desktop” reaches your Linux/BSD/other computer, probably via a nice entry in ~/.ssh/config
&lt;/p&gt;&lt;p&gt;
	Then use free and lovely &lt;a href=&quot;https://rsync.samba.org/&quot;&gt;rsync&lt;/a&gt; to synchronize:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
rsync -av --delete desktop:/home/me /home/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
	When it’s done, verify that everything from /home/me on your Linux/BSD computer is in /home/me on your Mac.
	Then move a file from your Mac Desktop or Documents into /home/me/ and copy it back:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
rsync -av --delete /home/me desktop:/home/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
	When it’s done, verify that whatever you just added from your Mac is now on your Linux/BSD computer.
	I like to go back and forth a couple times to be absolutely sure it’s working correctly, to make sure I can rely on it.
&lt;/p&gt;&lt;p&gt;
	I use these two commands so much that I made them one-line shell scripts in /home/me/bin/ so I can just type “gethome” when I first open the Mac, or “puthome” when I’m done on the Mac.
&lt;/p&gt;&lt;p&gt;
	Note this also works very well for a &lt;strong&gt;remote copy of your home directory&lt;/strong&gt; on an encrypted remote server as described in &lt;a href=&quot;https://sive.rs/ti&quot;&gt;my “Tech Independence” page&lt;/a&gt;.
	In that case, the shell script has three steps:
&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;
	SSH into the remote server, so you can type the password to mount the encrypted partition.
&lt;/li&gt;&lt;li&gt;
	rsync -av --delete between them
&lt;/li&gt;&lt;li&gt;
	SSH into the remote server to unmount the encrypted partition.
&lt;/li&gt;&lt;/ol&gt;

&lt;h3&gt;
	Whatever’s not in /home/me is Mac-only
&lt;/h3&gt;
&lt;p&gt;
	This /home/me where you’ll keep everything of value — everything you share between computers.
	Whatever’s in your /Users/me directory but &lt;strong&gt;not&lt;/strong&gt; in your /Users/me/me is &lt;strong&gt;Mac-only&lt;/strong&gt;, and won’t be copied to your other computer.
	So whatever you have in the MacOS defaults of Desktop, Documents, Downloads, Library, Movies, Music, Pictures, and Public is only on the Mac.
	If you want to sync anything in there to your other computer, move it to /home/me/
&lt;/p&gt;&lt;p&gt;
	I actually like this if I’m travelling with the Mac laptop and want to download some music or movies into ~/Music or ~/Movies but wouldn’t want to sync that 20GB of media to my other computer.
&lt;/p&gt;&lt;p&gt;
	But most of all, I love having /home/me be the same on all computers, so I can be writing or coding on the Mac laptop on a train, then get home and type “puthome”, and get back to work on my desktop.
&lt;/p&gt;&lt;p&gt;
	Another nice side-effect of keeping them in sync is if I accidently delete a file on one computer, I can pick up the other one to restore it.
	Or if the entire computer is stolen or destroyed, I can just curse a bit, then get back to work on the other.
&lt;/p&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/now2</id>
	<title>How and why to make a /now page on your site</title> 
	<published>2024-05-18T04:00:00Z</published>
	<updated>2024-05-18T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/now2"/> 
	<summary type="text">Background</summary> 
	<content type="html">&lt;h2&gt;
	Background
&lt;/h2&gt;
&lt;p&gt;
	I used to wonder what my friend &lt;a href=&quot;https://bennylewis.com/&quot;&gt;Benny Lewis&lt;/a&gt; was doing.
	He has &lt;a href=&quot;https://www.fluentin3months.com/&quot;&gt;a website&lt;/a&gt; and social media accounts, but neither gave &lt;strong&gt;an overview of what he’s doing now&lt;/strong&gt;.
&lt;/p&gt;&lt;p&gt;
	Then I realized some people might wonder the same about me.
	So in 2015, I made &lt;a href=&quot;https://sive.rs/now&quot;&gt;a /now page&lt;/a&gt; on my website, saying &lt;strong&gt;what I’d tell a friend I hadn’t seen in a year&lt;/strong&gt;.
&lt;/p&gt;&lt;p&gt;
	It has a nice side-effect of being a public declaration of priorities.
	It’s a good link to give people when &lt;a href=&quot;https://sive.rs/n&quot;&gt;saying no&lt;/a&gt; to invitations and distractions.
&lt;/p&gt;&lt;p&gt;
	Word spread, and soon &lt;strong&gt;&lt;a href=&quot;https://nownownow.com/&quot;&gt;hundreds of people&lt;/a&gt; had a /now page on their personal website&lt;/strong&gt;.
	So I made a site to showcase them all: &lt;strong&gt;&lt;a href=&quot;https://nownownow.com/&quot;&gt;nownownow.com&lt;/a&gt;&lt;/strong&gt; — (a static site generated by &lt;a href=&quot;https://sive.rs/pg2&quot;&gt;PostgreSQL functions&lt;/a&gt;.)
	It currently has over &lt;a href=&quot;https://nownownow.com/&quot;&gt;2300 people&lt;/a&gt; worldwide.
	This week I added browse by location and search.
&lt;/p&gt;
&lt;h2&gt;
	Got a personal website? Add a /now page
&lt;/h2&gt;
&lt;p&gt;
	The three main ingredients are:
&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;
	a page, usually at URL /now, linked from your main menu, usually alongside /about
&lt;/li&gt;&lt;li&gt;
	an overview of what’s going on with you — what you’d tell a friend you hadn’t seen in a year
&lt;/li&gt;&lt;li&gt;
	the date it was last updated
&lt;/li&gt;&lt;/ol&gt;
&lt;h2&gt;
	WordPress instructions:
&lt;/h2&gt;
&lt;p&gt;
	In the left menu, under “&lt;strong&gt;Pages&lt;/strong&gt;”, click “&lt;strong&gt;Add New Page&lt;/strong&gt;”.
	Then, where it says “Add title”, replace that with just three letters: &lt;strong&gt;now&lt;/strong&gt;.
	That will ensure the URL is /now, and after it’s posted, you can change the title to “What I’m doing now” or whatever.
&lt;/p&gt;
&lt;img src=&quot;https://m.sive.rs/images/now-wordpress-1.png&quot; alt=&quot;screenshot to make /now page on wordpress&quot;&gt;
&lt;br&gt;
&lt;img src=&quot;https://m.sive.rs/images/now-wordpress-2.png&quot; alt=&quot;screenshot to make /now page on wordpress&quot;&gt;
&lt;h2&gt;
	Wix instructions:
&lt;/h2&gt;
&lt;p&gt;
	On the left, under “&lt;strong&gt;Site Menu&lt;/strong&gt;”, click “&lt;strong&gt;+ Add Page&lt;/strong&gt;”.
	Call it &lt;strong&gt;Now&lt;/strong&gt;, next to your “About” page.
	Click the (…) to its right, then to “&lt;strong&gt;SEO basics&lt;/strong&gt;”, to edit “&lt;strong&gt;URL slug&lt;/strong&gt;” and make sure it’s just the three letters: now
&lt;/p&gt;
&lt;img src=&quot;https://m.sive.rs/images/now-wix-1.png&quot; alt=&quot;screenshot to make /now page on wix&quot;&gt;
&lt;br&gt;
&lt;img src=&quot;https://m.sive.rs/images/now-wix-2.png&quot; alt=&quot;screenshot to make /now page on wix&quot;&gt;
&lt;br&gt;
&lt;img src=&quot;https://m.sive.rs/images/now-wix-3.png&quot; alt=&quot;screenshot to make /now page on wix&quot;&gt;
&lt;/p&gt;
&lt;h2&gt;
	No website yet? Use &lt;a href=&quot;https://bearblog.dev/&quot;&gt;Bear&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;
	If you don’t have a personal website yet, I highly recommend &lt;a href=&quot;https://bearblog.dev/&quot;&gt;Bear&lt;/a&gt; at &lt;a href=&quot;https://bearblog.dev/&quot;&gt;BearBlog.dev&lt;/a&gt;.
	It’s so simple, clean, and free.
	The owner and creator, &lt;a href=&quot;https://herman.bearblog.dev/&quot;&gt;Herman in South Africa&lt;/a&gt;, runs it himself with great love.
	And no investors so no &lt;a href=&quot;https://en.wikipedia.org/wiki/Enshittification&quot;&gt;enshittification&lt;/a&gt;.
	He plans to keep it alive &lt;a href=&quot;https://herman.bearblog.dev/building-software-to-last-forever/&quot;&gt;forever&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	I believe in it so much that I told Herman I would be its godfather.
	If he ever can’t (or doesn’t want to) run it anymore, I will help run it, or fund a foundation to keep it alive.
&lt;/p&gt;&lt;p&gt;
	To create a /now page on Bear, click “&lt;strong&gt;Pages&lt;/strong&gt;”, then “&lt;strong&gt;New page&lt;/strong&gt;”.
	Then, after it says “&lt;strong&gt;title:&lt;/strong&gt;”, type just three letters: &lt;strong&gt;now&lt;/strong&gt;.
	That will ensure the URL is /now, and after it’s posted, you can change the title to “What I’m doing now” or whatever.
&lt;/p&gt;
&lt;img src=&quot;https://m.sive.rs/images/now-bear-1.png&quot; alt=&quot;screenshot to make /now page on bearblog.dev&quot;&gt;
&lt;br&gt;
&lt;img src=&quot;https://m.sive.rs/images/now-bear-2.png&quot; alt=&quot;screenshot to make /now page on bearblog.dev&quot;&gt;
&lt;h2&gt;
	Got a /now page? I’ll add you to &lt;a href=&quot;https://nownownow.com/&quot;&gt;nownownow.com&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;
	Once it’s live, just &lt;a href=&quot;https://sive.rs/contact&quot;&gt;email me your URL&lt;/a&gt;, and I add it (by hand) to &lt;a href=&quot;https://nownownow.com/&quot;&gt;nownownow.com&lt;/a&gt;.
	(This is also a good time to say hello, if you haven’t yet. I read and reply to every email.)
&lt;/p&gt;
&lt;a href=&quot;http://nownownow.com/&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;https://m.sive.rs/images/nowclock.jpg&quot;&gt;&lt;/a&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/learn-js</id>
	<title>How to learn JavaScript</title> 
	<published>2024-03-08T05:00:00Z</published>
	<updated>2024-03-08T05:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/learn-js"/> 
	<summary type="text">Since I mentioned that I learned JavaScript, people have asked me how and what I recommend.</summary> 
	<content type="html">&lt;p&gt;
	Since I &lt;a href=&quot;https://sive.rs/prog&quot;&gt;mentioned&lt;/a&gt; that I learned JavaScript, people have asked me how and what I recommend.
	So here’s my experience and best advice for 2024.
&lt;/p&gt;&lt;h3&gt;
	Learn plain JavaScript
&lt;/h3&gt;&lt;p&gt;
	First, it’s important to &lt;strong&gt;learn plain JavaScript&lt;/strong&gt;.
&lt;/p&gt;&lt;p&gt;
	Don’t depend on a library of shortcuts.
	JavaScript will be around for many years, while libraries and frameworks come and go every year.
&lt;/p&gt;&lt;h3&gt;
	How to begin
&lt;/h3&gt;&lt;p&gt;
	Don’t just learn from little bits of tutorials, tips, or tricks online.
	Instead, you want a real foundation and solid understanding.
	Then all your future learning will be so much faster.
&lt;/p&gt;&lt;p&gt;&lt;strong&gt;
	Start with the book: “&lt;a href=&quot;https://eloquentjavascript.net/&quot;&gt;Eloquent JavaScript&lt;/a&gt;”.
&lt;/strong&gt;
	It’s free to read there on his website.
	It’s deep and thorough.
	A great start-to-finish JavaScript tutorial.
	Relax.
	Focus.
	Do it in order.
	You’ll know more than most once you get to the end.
&lt;/p&gt;&lt;p&gt;&lt;strong&gt;
	Do &lt;a href=&quot;https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures-v8/&quot;&gt;Free Code Camp&lt;/a&gt;.
&lt;/strong&gt;
	Someone who had 600 JavaScript learners &lt;a href=&quot;https://news.ycombinator.com/item?id=11048409&quot;&gt;said&lt;/a&gt; Free Code Camp had the best results.
	Do it at the same time as you’re reading books and articles.
	Learning sticks better when coming through different senses.
&lt;/p&gt;&lt;h3&gt;
	Make it stick
&lt;/h3&gt;&lt;p&gt;
	I highly recommend you &lt;strong&gt;write down every new thing you learn&lt;/strong&gt;, ideally into a flashcard program, &lt;a href=&quot;https://sive.rs/srs&quot;&gt;like this&lt;/a&gt;.
	It’s a lot to digest, so you’ll have to remind yourself of what you’ve learned, or you’ll immediately forget.
&lt;/p&gt;&lt;h3&gt;
	Avoid the shortcuts
&lt;/h3&gt;&lt;p&gt;
	When solving a problem, everyone will point you to some pre-made solution.
	“Use jQuery! Use React! Use this library and save yourself some typing!”
&lt;/p&gt;&lt;p&gt;&lt;strong&gt;
	But no!
	Not yet!
	Do it the hard way.
	Solve everything yourself with plain JavaScript.
&lt;/strong&gt;
	It’s the best way to learn.
&lt;/p&gt;&lt;h3&gt;
	What next?
&lt;/h3&gt;&lt;p&gt;
	If you had a web app in mind, start building it now.
	After you wrestle with using plain “vanilla” JavaScript, then learn &lt;a href=&quot;https://react.dev/&quot;&gt;React&lt;/a&gt; to see the shortcuts it gives you.
&lt;/p&gt;&lt;p&gt;
	If you want a good-paying job, you can &lt;a href=&quot;https://sive.rs/gethired&quot;&gt;get hired&lt;/a&gt; almost anywhere.
	Since you know the real JavaScript foundations more than most people, you’re very valuable.
	You can quickly learn whatever framework they’re using.
&lt;/p&gt;&lt;h3&gt;
	Any other suggestions?
	What worked for you?
&lt;/h3&gt;&lt;p&gt;
	Please share any other suggestions or experiences, here:
&lt;/p&gt;
&lt;img alt=&quot;&quot; src=&quot;https://m.sive.rs/images/js.png&quot;&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/backup</id>
	<title>How I backup</title> 
	<published>2024-02-27T05:00:00Z</published>
	<updated>2024-02-27T05:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/backup"/> 
	<summary type="text">Some people have asked, so here is how I do my backups.</summary> 
	<content type="html">&lt;p&gt;
	Some people have asked, so here is how I do my backups.
	It takes me about &lt;strong&gt;ten seconds per day&lt;/strong&gt; and &lt;strong&gt;five minutes per month&lt;/strong&gt; to maintain.
&lt;/p&gt;&lt;p&gt;
	It works well for me, but I’m always open to suggestions.
	Just &lt;a href=&quot;https://sive.rs/contact&quot;&gt;email me&lt;/a&gt; with any ideas or questions.
&lt;/p&gt;

&lt;h2&gt;
	every-day documents (~40 GB)
&lt;/h2&gt;
&lt;p&gt;
	Files I use and change every day: documents, emails, code, diary, ideas, website, accounts, etc.
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
	I have a desktop and a laptop, so I keep this ~40 GB directory cloned with rsync every day or so between them.
	Whenever I turn on one computer, I sync it from the other.
&lt;/li&gt;&lt;li&gt;
	Daily rsync to an encrypted Linux USB stick attached to the desktop.
&lt;/li&gt;&lt;li&gt;
	Daily rsync to an encrypted MacOS USB stick attached to the laptop.
&lt;/li&gt;&lt;li&gt;
	Daily rsync to an encrypted ZFS SSD inside the desktop.
&lt;/li&gt;&lt;li&gt;
	Daily rsync to OpenBSD remote attached storage at vultr.com as &lt;a href=&quot;https://sive.rs/ti&quot;&gt;described at my “Tech Independence” page&lt;/a&gt;.
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;
	I’ve written shortcuts for these rsync commands so it’s really as simple as me typing &lt;code&gt;bkz&lt;/code&gt; or &lt;code&gt;bkstick&lt;/code&gt; in the terminal as I’m working anyway.
	I do it a few times a day, especially if I’ve just made or saved something of value.
	And always right before I shut down the computer, which I do almost any time I step away from it for more than a few minutes.
	That’s why I say it’s like ten seconds a day, just typing that command occasionally.
&lt;/p&gt;

&lt;h2&gt;
	keepsakes (~3 TB)
&lt;/h2&gt;
&lt;p&gt;
	Rarely-accessed files I want to keep forever: videos and photos of my kid, music and film collection.
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
	Three different external 4TB 2.5&amp;quot; drives, in three different formats: ZFS, OpenBSD, MacOS.
&lt;/li&gt;&lt;li&gt;
	Every month or so, I connect them via USB cable, and rsync everything to each of them, bringing one down to a safe deposit box downtown, and taking out the one that was there from my last visit.
&lt;/li&gt;&lt;li&gt;
	5TB at &lt;a href=&quot;https://www.hetzner.com/storage/storage-box&quot;&gt;Hetzner Storage Box&lt;/a&gt;, I rsync remotely.
&lt;/li&gt;&lt;li&gt;
	4TB at &lt;a href=&quot;https://zfs.rent/&quot;&gt;ZFS.rent&lt;/a&gt;, a ZFS encrypted clone.
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;
	So that’s five copies of my keepsakes in four different locations.
	Each one also has a recent copy of my every-day documents from above.
	So a few minutes a month to connect the USB drives, and I only do the safe deposit box when I’m going that way anyway, and only takes a couple minutes.
&lt;/p&gt;&lt;p&gt;
	In the days of spinning-platter hard drives, they used to die surprisingly often, so I like the security of many copies.
	But it also helps to have some deliberately behind others, so on the rare case where I’ve deleted something, then rsynced my dailies and even my monthly, then realized I need that thing I deleted, then I know the drive in the safe deposit box has it since I haven’t updated that one in a couple months.
&lt;/p&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/anon</id>
	<title>Want anonymity? Make a persona not a mystery.</title> 
	<published>2023-02-02T05:00:00Z</published>
	<updated>2023-02-02T05:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/anon"/> 
	<summary type="text">Because of my open inbox, I meet a lot of strangers.</summary> 
	<content type="html">&lt;p&gt;
	Because of my &lt;a href=&quot;https://sive.rs/contact&quot;&gt;open inbox&lt;/a&gt;, I meet a lot of strangers.
	I love it.
	Almost everyone tells me who and where they are in the world.
&lt;strong&gt;
	If they don’t, I wonder.
&lt;/strong&gt;
&lt;/p&gt;&lt;p&gt;
	Am I talking with someone from Australia? Philippines? Brazil?
	Are they 20 or 60?
	Male or female?
	It doesn’t really matter, but the brain can’t help wondering.
	It’s human nature to want to know who’s speaking.
&lt;strong&gt;
	If they don’t say, it creates a mystery.
&lt;/strong&gt;
&lt;/p&gt;&lt;p&gt;
	Once people start wondering, they need to know.
&lt;a href=&quot;https://sive.rs/mystery&quot;&gt;
	Mysteries are intriguing.
&lt;/a&gt;
	They’re unsettling.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	That’s a problem if you really want to be anonymous.
&lt;/strong&gt;
	If you defiantly refuse to say who you are, it can make people angry that you’re upsetting social reciprocity.
	You know who they are, but they don’t know who you are.
	It feels rude.
	An obsessive personality might make it their damn mission to figure out who you are!
	You don’t want that.
&lt;/p&gt;&lt;p&gt;
	So for real anonymity, don’t create a mystery.
&lt;strong&gt;
	Create a believable persona.
&lt;/strong&gt;
	Then nobody will wonder.
&lt;/p&gt;&lt;p&gt;
	If you don’t want any attention, just pick a &lt;a href=&quot;https://www.verymanynames.com/which-name-is-most-common-in-the-world/&quot;&gt;very common name&lt;/a&gt; like &lt;a href=&quot;https://www.facebook.com/public/Mary-Kim&quot;&gt;Mary Kim&lt;/a&gt; or &lt;a href=&quot;https://www.facebook.com/public/Adam-Johnson&quot;&gt;Adam Johnson&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	Use an &lt;a href=&quot;https://duckduckgo.com/?q=this+person+does+not+exist&quot;&gt;AI face generator&lt;/a&gt; to create a completely believable face to match your new name.
	Download it once and use it everywhere.
	Run it through &lt;a href=&quot;https://duckduckgo.com/?q=ai+face+aging&quot;&gt;face aging software&lt;/a&gt; to use this same persona for the rest of your life.
&lt;/p&gt;&lt;p&gt;
	Pick a city and say it’s your location, to avoid that question too.
&lt;/p&gt;&lt;p&gt;
	For email, &lt;a href=&quot;https://mailbox.org/&quot;&gt;Mailbox.org&lt;/a&gt; is great, and doesn’t care who you are.
&lt;/p&gt;&lt;p&gt;
	Create social media profiles with your new name, email, city, and face.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	Nobody will wonder who you are if you answer that question.
&lt;/strong&gt;
	Instead of block and battle, deflect and settle.
	That’s better anonymity.
&lt;/p&gt;&lt;p&gt;
	But &lt;strong&gt;if you want to be both anonymous and &lt;a href=&quot;https://sive.rs/publicu&quot;&gt;famous&lt;/a&gt;&lt;/strong&gt;, pick a name that is rare but believable.
	Cool but not too cool.
	Tom Kahlo.
	Keaton Carolina.
	Miles Wenley.
	Pick a name that has &lt;a href=&quot;https://porkbun.com/products/domains&quot;&gt;the .com domain&lt;/a&gt; available, so you can really brand it.
&lt;/p&gt;&lt;p&gt;
	Now you can create anything online freely, and nobody will doubt your identity.
	Create and post a back-story to answer (instead of avoid) the frequently asked questions.
	Then, instead of wondering who you really are, they can focus on what you’re really saying.
&lt;/p&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/shc</id>
	<title>Static HTML comments</title> 
	<published>2022-10-08T04:00:00Z</published>
	<updated>2022-10-08T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/shc"/> 
	<summary type="text">If you have a static HTML website, but you want to include comments, here’s an interesting way to do it using PostgreSQL’s NOTIFY and LISTEN.</summary> 
	<content type="html">&lt;p&gt;
	If you have a static HTML website, but you want to include comments, here’s an interesting way to do it using PostgreSQL’s NOTIFY and LISTEN.
&lt;/p&gt;&lt;p&gt;
	The big idea is to &lt;strong&gt;write the comments as static HTML, only when comments change&lt;/strong&gt;, instead of doing a database query to display them every time.
	This prevents the “hug of death” if you get a burst of traffic.
&lt;/p&gt;&lt;p&gt;
	I’ve been doing it this way for over six years, and it works great.
	Here’s the recipe, using Ruby as the glue, though you could use any scripting language.
&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;
	PostgreSQL database table for comments
&lt;/li&gt;&lt;li&gt;
	Ruby receives form posts, inserts into database
&lt;/li&gt;&lt;li&gt;
	When comments change, PostgreSQL trigger sends NOTIFY
&lt;/li&gt;&lt;li&gt;
	Ruby runs PostgreSQL LISTEN, exporting updated comments to HTML
&lt;/li&gt;&lt;li&gt;
	JavaScript on static page includes HTML
&lt;/li&gt;&lt;/ol&gt;

&lt;h2&gt;
	PostgreSQL database table for comments
&lt;/h2&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;create table comments (
  id integer primary key generated by default as identity,
  uri text,
  created_at date default current_date,
  name text,
  email text,
  comment text
);
create index on comments(uri);
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/comments-table.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;

&lt;h2&gt;
	Ruby receives form posts, inserts into database
&lt;/h2&gt;

&lt;p&gt;
	Put this on any HTML page where you want comments:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;section id=&amp;quot;comments&amp;quot;&amp;gt;&amp;lt;/section&amp;gt;
&amp;lt;script src=&amp;quot;/comments.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/comments.html.txt&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;

&lt;p&gt;
	Put this next code in your Nginx config, to send /comments to localhost.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;location = /comments {
  proxy_pass http://127.0.0.1:4567;
}
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/comments.nginx.conf&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;

&lt;p&gt;
	Ruby Sinatra receives form posts.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;require &amp;#39;pg&amp;#39;
require &amp;#39;sinatra&amp;#39;
DB = PG::Connection.new(dbname: &amp;#39;test&amp;#39;, user: &amp;#39;tester&amp;#39;)

post &amp;#39;/comments&amp;#39; do
  DB.exec_params(&amp;quot;insert into comments
    (uri, name, email, comment)
    values ($1, $2, $3, $4)&amp;quot;,
    [params[:uri], params[:name], params[:email], params[:comment]])
  redirect to(request.env[&amp;#39;HTTP_REFERER&amp;#39;])
end
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/comments-route.rb&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;

&lt;p&gt;
	Run that in a terminal on the server, and it should default to listen on port 4567.
&lt;/p&gt;

&lt;h2&gt;
	When comments change, PostgreSQL trigger sends NOTIFY
&lt;/h2&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;create function comments_changed() returns trigger as $$
begin
  perform pg_notify(&amp;#39;comments_changed&amp;#39;, new.uri);
  return new;
end;
$$ language plpgsql;
create trigger comments_changed after insert or update on comments
for each row execute procedure comments_changed();
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/comments_changed.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;

&lt;p&gt;
	Load that function into the PostgreSQL database that has your comments table.
&lt;/p&gt;&lt;p&gt;
	It sends the listener (below) a notification that comments for &lt;strong&gt;this URI&lt;/strong&gt; have changed.
	Then the listener will re-output comments just for this URI, instead of all.
&lt;/p&gt;

&lt;h2&gt;
	Ruby runs PostgreSQL LISTEN, exporting updated comments to HTML
&lt;/h2&gt;
&lt;p&gt;
	Make a directory in your web root called /commentcache/, to hold the static comments.
&lt;/p&gt;&lt;p&gt;
	Then keep this Ruby script running in a terminal to listen for database changes, and write the updated comments to disk as HTML.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;require &amp;#39;pg&amp;#39;
DB = PG::Connection.new(dbname: &amp;#39;test&amp;#39;, user: &amp;#39;tester&amp;#39;)
BASEDIR = &amp;#39;/var/www/htdocs/commentcache/&amp;#39; # directory in your web root

# a single comment list entry, used in ol map, below
def li(row)
  &amp;#39;&amp;lt;li&amp;gt;&amp;lt;cite&amp;gt;%s (%s)&amp;lt;/cite&amp;gt;&amp;lt;p&amp;gt;%s&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;#39; %
    [row[&amp;#39;name&amp;#39;], row[&amp;#39;created_at&amp;#39;], row[&amp;#39;comment&amp;#39;]]
end

# top-level map of database rows into HTML list
def ol(rows)
  rows.inject(&amp;#39;&amp;#39;) {|html, row| html += li(row) ; html}
end

# write comments to disk for this URI
def save_comments(uri)
  rows = DB.exec_params(&amp;quot;select name, created_at, comment
    from comments where uri = $1 order by id&amp;quot;, [uri]).to_a
  File.open(BASEDIR + uri, &amp;#39;w&amp;#39;) do |f|
    f.puts ol(rows)
  end
end

# first write them all
DB.exec(&amp;quot;select distinct(uri) from comments&amp;quot;).each do |r|
  save_comments(r[&amp;#39;uri&amp;#39;])
end

# listen for changes. re-write when changed
DB.exec(&amp;#39;listen comments_changed&amp;#39;)
while true do
  DB.wait_for_notify do |event, pid, uri|
    save_comments(uri)
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/comments-listen.rb&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;

&lt;h2&gt;
	JavaScript on static page includes current HTML when viewed
&lt;/h2&gt;
&lt;p&gt;
	Use JavaScript to show the form to post a comment, and load the list of comments from the /commentcache/ path.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;function showForm(uri) {
  document.getElementById(&amp;#39;comments&amp;#39;).innerHTML = `
&amp;lt;header&amp;gt;&amp;lt;h1&amp;gt;Comments:&amp;lt;/h1&amp;gt;&amp;lt;/header&amp;gt;
&amp;lt;form method=&amp;quot;post&amp;quot; action=&amp;quot;/comments&amp;quot;&amp;gt;
&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;uri&amp;quot; value=&amp;quot;${uri}&amp;quot;&amp;gt;
&amp;lt;label for=&amp;quot;name&amp;quot;&amp;gt;Your Name&amp;lt;/label&amp;gt;
&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;name&amp;quot; id=&amp;quot;name&amp;quot; required&amp;gt;
&amp;lt;label for=&amp;quot;email&amp;quot;&amp;gt;Your Email&amp;lt;/label&amp;gt;
&amp;lt;input type=&amp;quot;email&amp;quot; name=&amp;quot;email&amp;quot; id=&amp;quot;email&amp;quot; required&amp;gt;
&amp;lt;label for=&amp;quot;comment&amp;quot;&amp;gt;Comment&amp;lt;/label&amp;gt;
&amp;lt;textarea name=&amp;quot;comment&amp;quot; id=&amp;quot;comment&amp;quot; cols=&amp;quot;80&amp;quot; rows=&amp;quot;10&amp;quot; required&amp;gt;&amp;lt;/textarea&amp;gt;
&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;post comment&amp;quot;&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;ol id=&amp;quot;commentlist&amp;quot;&amp;gt;&amp;lt;/ol&amp;gt;`;
}

function getComments(uri) {
  try {
    const xhr = new XMLHttpRequest();
    xhr.open(&amp;#39;get&amp;#39;, &amp;#39;/commentcache/&amp;#39; + uri);
    xhr.send(null);
    xhr.onload = function() {
      if (xhr.status === 200) {
        document.getElementById(&amp;#39;commentlist&amp;#39;).innerHTML = xhr.responseText;
      }
    };
  } catch(e) { }
}

// /blog/topic/page.html uri = &amp;#39;blog_topic_page.html&amp;#39; for filesystem
const uri = location.pathname.substring(1).replace(/\//g, &amp;#39;_&amp;#39;);
showForm(uri);
getComments(uri);
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/comments.js&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;

&lt;p&gt;
	That’s all.
	I’ve simplified it a bit from my real usage, where I have constraints and checks that would have distracted from the core point of this example.
&lt;/p&gt;&lt;p&gt;
	There are other ways to do it.
	The NOTIFY and LISTEN isn’t necessary.
	The Ruby Sinatra route that receives the posted comment could just write the HTML to disk immediately.
	But I have other scripts that delete and update comments, and I like how &lt;strong&gt;the combination of NOTIFY trigger and LISTEN script always keeps them updated&lt;/strong&gt; on disk.
&lt;/p&gt;&lt;p&gt;
	Another interesting approach would be to write the comments into each HTML file directly, instead of in a separate file, so you wouldn’t need JavaScript at all.
&lt;/p&gt;

&lt;h2&gt;
	Optional upgrade: NOTIFY on delete
&lt;/h2&gt;
&lt;p&gt;
	I simplified the PostgreSQL trigger for the example, but with a few more lines of code, you can use the same trigger to notify of deleted comments, too.
	The value of a deleted row is in “old”, whereas inserted and updated is in “new”, so we have to make a uri variable, and an if/then/else to know which to use.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;create or replace function comments_changed() returns trigger as $$
declare
  uri text;
begin
  if tg_op = &amp;#39;DELETE&amp;#39; then
    uri = old.uri;
  else
    uri = new.uri;
  end if;
  perform pg_notify(&amp;#39;comments_changed&amp;#39;, uri);
  return old;
end;
$$ language plpgsql;
create trigger comments_changed after insert or update or delete on comments
for each row execute procedure comments_changed();
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/comments_changed2.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/com</id>
	<title>Find a good available .com domain</title> 
	<published>2022-06-08T04:00:00Z</published>
	<updated>2022-06-08T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/com"/> 
	<summary type="text">If you need a new domain name, and you want a .com, and you don’t want to type random ideas into a registrar search, here’s a way to do it.</summary> 
	<content type="html">&lt;p&gt;
	If you need a new domain name, and you want a &lt;strong&gt;.com&lt;/strong&gt;, and you don’t want to type random ideas into a registrar search, here’s a way to do it.
&lt;/p&gt;
&lt;h2&gt;
	Download the list of all registered .com domains
&lt;/h2&gt;
&lt;p&gt;
	First, apply for access to the zone file, using ICANN’s Centralized Zone Data Service (CZDS) at &lt;a href=&quot;https://czds.icann.org/&quot;&gt;https://czds.icann.org/&lt;/a&gt;.
	It’s free, but takes a few days to get approved.
	&lt;a href=&quot;https://www.verisign.com/en_US/channel-resources/domain-registry-products/zone-file/index.xhtml&quot;&gt;Read more about it here&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	Once approved, they email you a password to log in and download the file called &lt;strong&gt;com.txt.gz&lt;/strong&gt;.
&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
$ du -hs com.txt.gz # 4.6GB compressed
4.6G    com.txt.gz
$ gunzip com.txt.gz # uncompress and wait
$ du -hs com.txt
23.0G   com.txt     # 23 gigs uncompressed
$ wc -l com.txt
404261754 com.txt   # 404 million lines
&lt;/pre&gt;&lt;/code&gt;

&lt;h2&gt;
	Extract the unique names
&lt;/h2&gt;
&lt;p&gt;
	&lt;strong&gt;com.txt&lt;/strong&gt; has 404 million lines like this:
&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
zombahomes.com. 172800  in      ns      ns2.tierra.net.
zombai.com.     172800  in      ns      ns1.parkingcrew.net.
zombai.com.     172800  in      ns      ns2.parkingcrew.net.
zombaid.com.    172800  in      ns      nsg1.namebrightdns.com.
zombaid.com.    172800  in      ns      nsg2.namebrightdns.com.
zombaimmo.com.  172800  in      ns      ns10.lwsdns.com.
zombaimmo.com.  172800  in      ns      ns11.lwsdns.com.
zombaimmo.com.  172800  in      ns      ns12.lwsdns.com.
zombaimmo.com.  172800  in      ns      ns17.lwsdns.com.
zombaio.com.    172800  in      ns      ns-1073.awsdns-06.org.
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;
	Domains usually have more than one entry.
	You need to extract the unique entries.
	And you only need the part before the “.com”.
&lt;/p&gt;&lt;p&gt;
	Here’s a Ruby script that loops through com.txt, gets the part before .com, skips it if duplicate, and outputs it if unique.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;domain = &amp;#39;&amp;#39;
File.open(&amp;#39;com.txt&amp;#39;, &amp;#39;r&amp;#39;) do |infile|
  File.open(&amp;#39;domains.txt&amp;#39;, &amp;#39;w&amp;#39;) do |outfile|
    while line = infile.gets
      temp = line[0...(line.index(&amp;#39;.com&amp;#39;))]
      next if temp == domain
      domain = temp
      outfile.puts domain
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/domain-parse.rb&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	“domains.txt” should now be about 162 million lines - (about 2.2GB) - that look like this:
&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
zombahomes
zombai
zombaid
zombaimmo
zombaio
&lt;/pre&gt;&lt;/code&gt;

&lt;h2&gt;
	Load it into SQLite, and index it.
&lt;/h2&gt;
&lt;code&gt;&lt;pre&gt;
$ sqlite3 domains.db 
sqlite&gt; create table domains(domain text);
sqlite&gt; .import &quot;domains.txt&quot; domains
sqlite&gt; create index dd on domains(domain);
&lt;/pre&gt;&lt;/code&gt;

&lt;h2&gt;
	Find available dictionary words
&lt;/h2&gt;
&lt;p&gt;
	If you’re on Mac, Linux, or &lt;a href=&quot;https://sive.rs/openbsd&quot;&gt;BSD&lt;/a&gt;, you should have a dictionary of words at &lt;strong&gt;/usr/share/dict/words&lt;/strong&gt;.
	See which of those words are available:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;require &amp;#39;sqlite3&amp;#39;
db = SQLite3::Database.new(&amp;#39;domains.db&amp;#39;)
query = db.prepare(&amp;#39;select domain from domains where domain = ?&amp;#39;)
File.readlines(&amp;#39;/usr/share/dict/words&amp;#39;).each do |word|
  rows = query.execute(word.downcase.strip)
  puts word unless rows.next
end
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/domain-word.rb&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	Run that, and you’ll have a list of 93,000 dictionary words that are available with the .com extension.
	Congratulations!
	Go to &lt;a href=&quot;https://porkbun.com/&quot;&gt;porkbun.com&lt;/a&gt; (a great little registrar) to register yours.
&lt;/p&gt;&lt;p&gt;
	You’ll find that some are not actually available because that “com.txt” file doesn’t list domains on hold, pending deletion, or without name servers.
&lt;/p&gt;

&lt;h2&gt;
	Combine short dictionary words
&lt;/h2&gt;
&lt;p&gt;
	If you are not excited that “electrotelethermometer.com” or “counterexcommunication.com” is available, maybe you would like a combination of two short words?
	Select only dictionary words up to four letters, then search for the combination.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;require &amp;#39;sqlite3&amp;#39;
words = File.readlines(&amp;#39;/usr/share/dict/words&amp;#39;).map(&amp;amp;:strip)
words.select! {|w| w.size &amp;lt;= 4}
db = SQLite3::Database.new(&amp;#39;domains.db&amp;#39;)
query = db.prepare(&amp;#39;select domain from domains where domain = ?&amp;#39;)
words.each do |word1|
  words.each do |word2|
    combo = (word1 + word2).downcase
    rows = query.execute(combo)
    puts combo unless rows.next
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/domain-words.rb&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;

&lt;h2&gt;
	Narrow it down to good words
&lt;/h2&gt;
&lt;p&gt;
	If you ran that last script, you’ll get tens of millions of available domains like “knabtuik.com” because there are many unknown, ugly, and useless short words.
&lt;/p&gt;&lt;p&gt;
	So make a new file called “&lt;strong&gt;goodwords.txt&lt;/strong&gt;” of only three and four letter words, using grep:
&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
$ grep &quot;^...$&quot; /usr/share/dict/words &gt;&gt; goodwords.txt
$ grep &quot;^....$&quot; /usr/share/dict/words &gt;&gt; goodwords.txt
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;
	Edit that file by hand, deleting every word you would never want.
	(The less you keep, the better.)
	Then run that Ruby script again, combining just the good words:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;require &amp;#39;sqlite3&amp;#39;
words = File.readlines(&amp;#39;goodwords.txt&amp;#39;).map(&amp;amp;:strip)
db = SQLite3::Database.new(&amp;#39;domains.db&amp;#39;)
query = db.prepare(&amp;#39;select domain from domains where domain = ?&amp;#39;)
words.each do |word1|
  words.each do |word2|
    combo = (word1 + word2).downcase
    rows = query.execute(combo)
    puts combo unless rows.next
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/domain-goodwords.rb&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	Much better, right?
	A little time consuming, but worth it.
	This is how I found the name of my new translation service, &lt;a href=&quot;https://inchword.com/&quot;&gt;Inchword&lt;/a&gt;.
&lt;/p&gt;

&lt;h2&gt;
	Need it super-short and nerdy?
&lt;/h2&gt;
&lt;p&gt;
	One final hack is that there are tons of very-short .com domain names available in the format “letter-number-letter-number”.
	For example: &lt;strong&gt;q7r7.com&lt;/strong&gt; or &lt;strong&gt;e3p3.com&lt;/strong&gt;.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;require &amp;#39;sqlite3&amp;#39;
db = SQLite3::Database.new(&amp;#39;domains.db&amp;#39;)
query = db.prepare(&amp;#39;select domain from domains where domain = ?&amp;#39;)
(&amp;#39;a&amp;#39;..&amp;#39;z&amp;#39;).each do |a|
  (&amp;#39;0&amp;#39;..&amp;#39;9&amp;#39;).each do |b|
    (&amp;#39;a&amp;#39;..&amp;#39;z&amp;#39;).each do |c|
      (&amp;#39;0&amp;#39;..&amp;#39;9&amp;#39;).each do |d|
        combo = a + b + c + d
        rows = query.execute(combo)
        puts combo unless rows.next
      end
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/domain-letters.rb&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/su</id>
	<title>Short URLs: why and how</title> 
	<published>2022-05-08T04:00:00Z</published>
	<updated>2022-05-08T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/su"/> 
	<summary type="text">If you make your own website, consider making short URLs.</summary> 
	<content type="html">&lt;p&gt;
	If you make your own website, consider making short URLs.
&lt;/p&gt;&lt;p&gt;
	This is not about a URL shortener.
	This is about making your &lt;strong&gt;original URLs short&lt;/strong&gt; in the first place.
&lt;/p&gt;&lt;p&gt;
	Instead of:
&lt;br&gt;&lt;code&gt;yoursite.com/blog/2022/05/08/short-urls-why-and-how.html&lt;/code&gt;&lt;br&gt;
	… consider just:
&lt;br&gt;&lt;code&gt;yoursite.com/short&lt;/code&gt;
&lt;/p&gt;
&lt;h2&gt;
	Why?
&lt;/h2&gt;
&lt;p&gt;
	Short URLs matter for a few reasons:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
&lt;strong&gt;
	I can remember my URLs.
&lt;/strong&gt;
	I can avoid the search engine step.
	(No need to search when I already know the answer.)
	Which means…
&lt;/li&gt;&lt;li&gt;
&lt;strong&gt;
	I can type it or say it.
&lt;/strong&gt;
	Whether texting, answering an email, or talking to someone on the phone, I can say, “Go to &lt;a href=&quot;https://sive.rs/ff&quot;&gt;sive.rs/ff&lt;/a&gt; for my talk about the first follower.” or “My newest book is at &lt;a href=&quot;https://sive.rs/h&quot;&gt;sive.rs/h&lt;/a&gt;.”
	I do this often, so having memorable URLs, &lt;strong&gt;easy to type in full&lt;/strong&gt;, saves me a lot of searching.
&lt;/li&gt;&lt;li&gt;
&lt;strong&gt;
	They look nicer.
&lt;/strong&gt;
	They’re aesthetic.
	They show care.
	We should put something of beauty into the world, instead of creating &lt;a href=&quot;https://sive.rs/polut&quot;&gt;digital pollution&lt;/a&gt;.
&lt;/li&gt;&lt;li&gt;
&lt;strong&gt;
	They remove the middle-man.
&lt;/strong&gt;
	With &lt;em&gt;long&lt;/em&gt; URLs, people use those ugly social share buttons that promote (and further entrench) harmful social media sites, and add visual clutter to your site.
	Short URLs encourage people to copy and paste the URL directly, which lets them share it anywhere, instead of only the sites for which you have a share button.
&lt;/li&gt;&lt;li&gt;
&lt;strong&gt;
	They’re enough.
&lt;/strong&gt;
	Using 36 characters (a-z and 0-9):&lt;br&gt;
	2-character URLs give me 1296 (36²) unique combinations.&lt;br&gt;
	3-character URLs give me 46,656 (36³) unique combinations.&lt;br&gt;
	4-character URLs give me 1,679,616 (36⁴) unique combinations.&lt;br&gt;
	I don’t need more than that.
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;
	That doesn’t mean the shorter the better.
	If I can remember “&lt;code&gt;&lt;a href=&quot;https://sive.rs/plaintext&quot;&gt;/plaintext&lt;/a&gt;&lt;/code&gt;” easier than “&lt;code&gt;/pt&lt;/code&gt;”, then that’s a better choice.
&lt;/p&gt;
&lt;h2&gt;
	How?
&lt;/h2&gt;
&lt;p&gt;
	Here’s how I do it:
&lt;strong&gt;
	Save my HTML file as the URL name, with no extension.
&lt;/strong&gt;
	Instead of “&lt;code&gt;hi.html&lt;/code&gt;”, I save it as “&lt;code&gt;hi&lt;/code&gt;” in my public web root.
&lt;/p&gt;&lt;/p&gt;
	Then, assuming the Nginx web server, add this line to my http block:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;&lt;code&gt;default_type text/html;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;
	That’s it!
	That serves my files without extensions as HTML.
&lt;/p&gt;&lt;/p&gt;
	If I ever switch to a different system or server, there is always a way to pull up your HTML using a short URL.
&lt;/p&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/plaintext</id>
	<title>Write plain text files</title> 
	<published>2022-03-02T05:00:00Z</published>
	<updated>2022-03-02T05:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/plaintext"/> 
	<summary type="text">I write almost everything important in my life: thoughts, plans, notes, diaries, correspondence, code, articles, and entire books.</summary> 
	<content type="html">&lt;p&gt;
	I write almost everything important in my life: thoughts, plans, notes, diaries, correspondence, code, articles, and entire books.
&lt;/p&gt;&lt;p&gt;
	They are my extended memory — my noted self — my organized thoughts.
	I refer to them often.
	I search them, update them, and learn from them.
	I convert them into HTML to make websites, or LaTeX to make books.
&lt;/p&gt;&lt;p&gt;
	My written words are my most precious asset.
	They are also a history of my life.
&lt;strong&gt;
	That’s why I only use plain text files.
&lt;/strong&gt;
	They are the most reliable, flexible, and long-lasting option.
	Here’s why.
&lt;/p&gt;&lt;h2&gt;
	PORTABLE
&lt;/h2&gt;&lt;p&gt;
	I’ve brought my text files with me since 1990, from Mac to Windows to Linux to BSD, from PCs to laptops to tablets to Android to iOS to a tiny device the size of my thumb, and back again.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	Every device, including ones long gone, and ones not invented yet, can read and edit plain text.
&lt;/strong&gt;
	Whether future virtual reality, or a chip you can implant in your earlobe, plain text will be there.
	Will Microsoft Word?
	Evernote?
	Notion?
	Maybe.
	Maybe not.
&lt;/p&gt;&lt;p&gt;
	But plain text?
	Always.
	Everywhere.
&lt;/p&gt;&lt;h2&gt;
	UN-COMMERCIAL
&lt;/h2&gt;&lt;p&gt;
	Every few years a new company says you should use their special format.
	You have to pay them a monthly fee to use it — or keep all of your documents in their care.
	They offer some convenience or features, but at the cost of flexibility, portability, and independence.
&lt;/p&gt;&lt;p&gt;
	When you store your writing in one company’s unique format, then you need that program to access it.
	Then the economy takes a turn, they go out of business, and your work is trapped in an unusable format.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	You will outlive these companies.
&lt;/strong&gt;
	Your writing should outlive you.
	Depending on companies is not an option.
&lt;/p&gt;&lt;p&gt;
	Plain text is un-commercial.
	It removes you from the world of subscriptions and hype.
	There will always be plenty of free, non-commercial software in the public domain for reading and editing text files.
&lt;/p&gt;&lt;h2&gt;
	OFFLINE
&lt;/h2&gt;&lt;p&gt;
	There are places and times when you can’t get online.
	Don’t depend on any tool that needs an internet connection.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	There are great benefits to being intentionally offline and unreachable, to focus.
&lt;/strong&gt;
	It’s a super productivity boost.
	You need to be able to write, and have access to all your writing, during these times.
&lt;/p&gt;&lt;h2&gt;
	NO DEPENDENCIES
&lt;/h2&gt;&lt;p&gt;
	If you rely on Word, Evernote or Notion, for example, then you can’t work unless you have Word, Evernote, or Notion.
&lt;strong&gt;
	You are helpless without them.
&lt;/strong&gt;
	You are dependent.
&lt;/p&gt;&lt;p&gt;
	People tell me about more tools I could use in addition to my text files.
	But I don’t need or want anything else.
&lt;strong&gt;
	Plain text files and a basic text editor are enough.
	This is everything you need for great thinking and writing.
&lt;/strong&gt;
	(A paper notebook and pencil are enough, too.)
&lt;/p&gt;&lt;p&gt;
	If you only use plain text, you can work on any device, forever.
	The less you depend on, the better.
	Peace and focus come when you stop looking for more.
&lt;/p&gt;&lt;h2&gt;
	EASIEST TO CONVERT
&lt;/h2&gt;&lt;p&gt;
	Plain text can be converted into anything else.
&lt;/p&gt;&lt;p&gt;
	HTML, Markdown, JSON, LaTeX, and many other standard formats, are just plain text.
	I’ve written four books and four hundred blog posts in plain text.
&lt;/p&gt;&lt;p&gt;
	You can make your own personal formats in your plain text files.
	Maybe in each diary entry, the first two lines are like:
&lt;/p&gt;&lt;pre&gt;
date: 2022-02-28
tags: where-to-live, kids, dog, anxious
&lt;/pre&gt;&lt;p&gt;
	Then it’s easy to use any little scripting language like Ruby, Python, or JavaScript to grab the date and tags, and use them for categorizing, sorting, renaming, archiving, or exporting.
&lt;/p&gt;&lt;p&gt;
	Or if you don’t want to do it yourself, then it’s easy to find someone who can.
	Anyone who’s been programming for more than a week should be able to do it easily.
&lt;/p&gt;&lt;h2&gt;
	NEED HIERARCHY?
&lt;/h2&gt;&lt;p&gt;
	Use directories — also known as folders.
	These are also good for keeping your text together with other files like images and audio.
&lt;/p&gt;&lt;pre&gt;
Documents/
Documents/Diary/
Documents/Diary/2022/
Documents/Diary/2022/2022-02-28.txt
Documents/Thoughts/
Documents/Thoughts/WhereToLive/
Documents/Thoughts/WhereToLive/2019-06-30.txt
Documents/Thoughts/WhereToLive/2020-01-18.txt
Documents/Ideas/
Documents/Ideas/MusicalChairs.txt
Documents/Ideas/NewHouse/
Documents/Ideas/NewHouse/Design/
Documents/Ideas/NewHouse/Design/entryway.jpg
Documents/Ideas/NewHouse/Design/roof.jpg
Documents/Ideas/NewHouse/Architect/
Documents/Ideas/NewHouse/Architect/JM_Lim.txt
Documents/Ideas/NewHouse/Architect/TPS_Inc.txt
&lt;/pre&gt;&lt;h2&gt;
	NEED VISUALS OR GRAPHICS?
&lt;/h2&gt;&lt;p&gt;
	Need visual mind-mapping with circles and lines?
	Maybe you do.
&lt;strong&gt;
	But maybe you don’t.
&lt;/strong&gt;
	Maybe it’s just another distraction, focusing on the tools instead of your thinking.
&lt;/p&gt;&lt;p&gt;
	I love that plain text files have no formatting to tinker with.
	A tab key, SHIFT KEY, and vertical line breaks can go a long way, keeping you writing instead of formatting.
&lt;/p&gt;&lt;p&gt;
	If you really need graphics, do your drawing using something else.
	Digital drawing into SVG files.
	Paper drawing, scanned into JPGs.
	Formats that aren’t owned by any company.
	Formats that will outlast you.
&lt;/p&gt;&lt;p&gt;
	Keep your graphics files alongside your text files.
	But keep your text as plain text.
&lt;/p&gt;&lt;h2&gt;
	CONCLUSION
&lt;/h2&gt;&lt;p&gt;
	Reliable, flexible, portable, independent, and long-lasting.
	Plain text files will be readable by future generations, hundreds of years from now.
&lt;/p&gt;&lt;p&gt;
	I especially enjoy the tranquility of their offline, non-commercial nature.
	They’re quiet.
	They’re focused.
	(As I aim to be.)
&lt;/p&gt;
&lt;img src=&quot;https://m.sive.rs/images/plaintext.png&quot; alt=&quot;screen shot of the text of this post&quot;&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/rand1</id>
	<title>Generate unique random values directly in the database</title> 
	<published>2022-03-01T05:00:00Z</published>
	<updated>2022-03-01T05:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/rand1"/> 
	<summary type="text">You often need to generate random strings, like for login cookies and unique entry codes.</summary> 
	<content type="html">&lt;p&gt;
	You often need to generate random strings, like for login cookies and unique entry codes.
&lt;/p&gt;&lt;p&gt;
	These strings always need to be stored in the database.
&lt;strong&gt;
	So make your life simpler by generating the random strings directly in the database.
&lt;/strong&gt;
	Here&#39;s a very handy PostgreSQL function to do it:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;create function gen_random_bytes(int) returns bytea as
&amp;#39;$libdir/pgcrypto&amp;#39;, &amp;#39;pg_random_bytes&amp;#39; language c strict;

create function random_string(len int) returns text as $$
declare
  chars text[] = &amp;#39;{0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}&amp;#39;;
  result text = &amp;#39;&amp;#39;;
  i int = 0;
  rand bytea;
begin
  -- generate secure random bytes and convert them to a string of chars.
  rand = gen_random_bytes($1);
  for i in 0..len-1 loop
    -- rand indexing is zero-based, chars is 1-based.
    result = result || chars[1 + (get_byte(rand, i) % array_length(chars, 1))];
  end loop;
  return result;
end;
$$ language plpgsql;
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/rand1-1.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	Give it a number: the length of the random string you want.
	It will return random alphanumeric text of that length.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;select random_string(8);
random_string 
───────────────
 yBuXga02

select random_string(8);
 random_string 
───────────────
 eP3X7yqe
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/rand1-2.txt&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	The chance of random clash is very small.
&lt;strong&gt;
	But you need to be completely sure that the new random string is unique — not already existing in that column in that table.
&lt;/strong&gt;
	So here&#39;s a function where you give it the string length, table name, and column name.
	It will return a random string confirmed to be unique — to not exist there already.
	It gets a random string, searches for it in that table and column, and if not found, returns it.
	Otherwise, if it is found, gets a new random string and loops back, trying again until not found.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;-- return random string confirmed to not exist in given tablename.colname
create function unique_random(len int, _table text, _col text) returns text as $$
declare
  result text;
  numrows int;
begin
  result = random_string(len);
  loop
    execute format(&amp;#39;select 1 from %I where %I = %L&amp;#39;, _table, _col, result);
    get diagnostics numrows = row_count;
    if numrows = 0 then
      return result; 
    end if;
    result = random_string(len);
  end loop;
end;
$$ language plpgsql;
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/rand1-3.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	I used to call functions like this &lt;a href=&quot;https://sive.rs/clean1&quot;&gt;using database triggers&lt;/a&gt;, called on any insert.
	But then I found out something surprisingly cool and so much simpler:
&lt;strong&gt;
	You can call functions as default values directly in table definitions.
&lt;/strong&gt;
&lt;/p&gt;&lt;p&gt;
	Look at this table, calling unique_random as its default value:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;create table things (
  code char(8) primary key default unique_random(8, &amp;#39;things&amp;#39;, &amp;#39;code&amp;#39;),
  name text
);
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/rand1-4.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	So simple and clear!
	To use it, you just do a regular insert, and it generates the guaranteed-unique default value.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;insert into things (name) values (&amp;#39;one&amp;#39;) returning *;

   code   │ name 
──────────┼──────
 nRSXbVWQ │ one

insert into things (name) values (&amp;#39;two&amp;#39;) returning *;

   code   │ name 
──────────┼──────
 EAS9wGcl │ two
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/rand1-5.txt&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	I&#39;ve found this particularly handy for creating login cookies:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;create table cookies (
  person_id int primary key,
  cookie char(32) unique default unique_random(32, &amp;#39;cookies&amp;#39;, &amp;#39;cookie&amp;#39;)
);
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/rand1-6.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	Instead of having your client code, your JavaScript, Python, Ruby or whatever, generating the random code, it&#39;s extra-nice to have this in your database directly, not only because it&#39;s cleaner, but because it saves repeated calls between your client code and database, confirming uniqueness.
	One simple insert of the person_id returns the unique and already-saved random cookie string:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;insert into cookies (person_id) values (1) returning *;

 person_id │              cookie              
───────────┼──────────────────────────────────
         1 │ 0P8Tp4wjXuTqCCh1NCR9XIom20z9IcYv
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/rand1-7.txt&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	Download the code at &lt;a href=&quot;https://sive.rs/code/rand1.sql&quot;&gt;/code/rand1.sql&lt;/a&gt;.
&lt;/p&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/clean1</id>
	<title>Database triggers to clean text inputs</title> 
	<published>2022-03-01T05:00:00Z</published>
	<updated>2022-03-01T05:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/clean1"/> 
	<summary type="text">Even smart people can accidently put bad data into a database.</summary> 
	<content type="html">&lt;p&gt;
	Even smart people can accidently put bad data into a database.
&lt;/p&gt;&lt;p&gt;
	We copy text from a web page, then paste it into a form, not realizing it also copied a space, tab, or newline character.
	Then your system thinks someone’s name is not “Jim” but “ Jim\n”.
&lt;/p&gt;&lt;p&gt;
	You could use JavaScript to sanitize all form inputs, but what about when you import a CSV file, or get data from an API?
&lt;/p&gt;&lt;p&gt;
	No, &lt;strong&gt;the best place for your data-cleaning functions is in the database itself&lt;/strong&gt;.
	So no matter what code is inserting or updating, a database trigger will sanitize it &lt;strong&gt;before saving&lt;/strong&gt;.
&lt;/p&gt;&lt;p&gt;
	Here’s a PostgreSQL example.
	Let’s make two tables, people and emails, so you can see how one function can be used by many triggers.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;create table people (
  id serial primary key,
  name text,
  code text
);

create table emails (
  id serial primary key,
  person_id integer not null references people(id),
  email text
);
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/clean1-1.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	Let’s make two simple functions:
&lt;/p&gt;&lt;ol&gt;&lt;li&gt;
	Remove all whitespace, then lowercase.
&lt;/li&gt;&lt;li&gt;
	Remove unwanted whitespace characters like tab and newline.
	Replace them all with a single space.
	Then trim spaces from the front and end.
&lt;/li&gt;&lt;/ol&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;-- remove all whitespace, then lowercase it
create function lower_no_space(text) returns text as $$
  select lower(regexp_replace($1, &amp;#39;\s&amp;#39;, &amp;#39;&amp;#39;, &amp;#39;g&amp;#39;));
$$ language sql;

-- replace all whitespace with single space, then trim start and end
create function no_extra_space(text) returns text as $$
  select btrim(regexp_replace($1, &amp;#39;\s+&amp;#39;, &amp;#39; &amp;#39;, &amp;#39;g&amp;#39;));
$$ language sql;
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/clean1-2.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	The function that removes all whitespace then lowercases, is good for email addresses and codes like checksums.
&lt;/p&gt;&lt;p&gt;
	The function that removes extra whitespace then trims, is good for many things like names, addresses, email subjects, and anywhere that needs to retain inner spaces.
	So “ New \t Zealand \n” will be “New Zealand”.
&lt;/p&gt;&lt;p&gt;
	Now make trigger functions that use your smaller re-usable cleaning functions.
	I find it best to make one trigger per table, sanitizing all fields on any insert or update.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;create function clean_people() returns trigger as $$
begin
  new.name = no_extra_space(new.name);
  new.code = lower_no_space(new.code);
  return new;
end;
$$ language plpgsql;
create trigger clean_people
before insert or update on people
for each row execute procedure clean_people();

create function clean_emails() returns trigger as $$
begin
  new.email = lower_no_space(new.email);
  return new;
end;
$$ language plpgsql;
create trigger clean_emails
before insert or update on emails
for each row execute procedure clean_emails();
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/clean1-3.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	The language is a little verbose, but oh well.
	The ugliness of the boilerplate code is made up for by the beautiful simplicity of having all of this handled in the database.
	It’s so nice to not have to sanitize form fields!
	Just toss the unwashed inputs at the database.
&lt;/p&gt;&lt;p&gt;
	Here, let’s give it some dirty data, and watch it come out clean.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;insert into people (name, code) values (e&amp;#39; \t \r \n Dr.  \n \r JM \t Lim \r\n&amp;#39;, &amp;#39;   XX o Z &amp;#39;) returning *;
-- id │    name    │ code 
--────┼────────────┼──────
--  1 │ Dr. JM Lim │ xxoz

insert into emails (person_id, email) values (1, e&amp;#39; \r\n \t DR. L @ JM Lim . com \n&amp;#39;) returning *;
-- id │ person_id │     email      
--────┼───────────┼────────────────
--  1 │         1 │ dr.l@jmlim.com
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/clean1-4.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	This approach has made my code much simpler, and data much cleaner.
&lt;/p&gt;&lt;p&gt;
	Download the full code example at &lt;a href=&quot;https://sive.rs/code/clean1.sql&quot;&gt;/code/clean1.sql&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	Also look at &lt;a href=&quot;https://sive.rs/recalc&quot;&gt;using triggers to ensure data integrity&lt;/a&gt;.
&lt;/p&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/api01</id>
	<title>Database functions to wrap logic and SQL queries</title> 
	<published>2022-02-28T05:00:00Z</published>
	<updated>2022-02-28T05:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/api01"/> 
	<summary type="text">When you make a database-backed app, you have some functions that need to run multiple database queries.</summary> 
	<content type="html">&lt;p&gt;
	When you make a database-backed app, you have some functions that need to run multiple database queries.
&lt;/p&gt;&lt;p&gt;
	For example, to move money from one account to another, you have to insert a negative amount into account #1, and a positive amount into account #2.
&lt;/p&gt;&lt;p&gt;
	Usually you do in your main code: your JavaScript, Python, Ruby, or whatever.
&lt;/p&gt;&lt;p&gt;
	But what if some future code bypasses your crucial “business logic” functions?
	New code could access the database directly, without going through your existing functions.
&lt;/p&gt;&lt;p&gt;
	Or what if you need to rewrite some code in a new language?
	You’ll have a lot of data to rewrite if all this data logic was kept in the surrounding code.
&lt;/p&gt;&lt;p&gt;
	I felt the pain of this at my last company, when we converted some old PHP code to Ruby.
	I had to rewrite so much of the logic.
&lt;/p&gt;&lt;p&gt;
	In hindsight, &lt;strong&gt;data logic should be in the database itself&lt;/strong&gt;.
&lt;/p&gt;&lt;p&gt;
	Simple logic that’s always needed to update the data (like the money-moving example) should be &lt;strong&gt;kept in database functions&lt;/strong&gt;.
	Then your surrounding code - your JavaScript, Python, Ruby, or whatever - can just call these database functions, and never need to be rewritten if you change languages to Swift, Kotlin, Elixir, or whatever.
&lt;/p&gt;&lt;h3&gt;
	Here’s a PostgreSQL example, from &lt;a href=&quot;https://sive.rs/recalc&quot;&gt;my previous post&lt;/a&gt;:
&lt;/h3&gt;&lt;p&gt;
	First, make three simple tables:
&lt;/p&gt;&lt;ol&gt;&lt;li&gt;
	Items with prices.
&lt;/li&gt;&lt;li&gt;
	Lineitems with quantities.
&lt;/li&gt;&lt;li&gt;
	Invoices with the total price.
&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;
	Create two example items, a $5 and a $9 item.
	And create invoice #1 for testing.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;create table items (
  id serial primary key,
  price int not null check (price &amp;gt; 0)
);

create table invoices (
  id serial primary key,
  total int
);

create table lineitems (
  invoice_id int not null references invoices(id),
  item_id int not null references items(id),
  quantity int not null check (quantity &amp;gt; 0),
  primary key (invoice_id, item_id)
);

-- example data:
insert into items (price) values (5);
insert into items (price) values (9);
insert into invoices (total) values (0);
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/recalc-1.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	If someone wants to &lt;strong&gt;add an item&lt;/strong&gt; to their cart, you need to first see if it’s in their cart already.
	If it’s not in their cart, &lt;strong&gt;insert&lt;/strong&gt; it.
	But if that Item is in their cart, you need to &lt;strong&gt;update&lt;/strong&gt; it, to add the new quantity to their existing quantity.
&lt;/p&gt;&lt;p&gt;
	So wrap all that logic in a simple &lt;strong&gt;function called cart_add&lt;/strong&gt;.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;create function cart_add(inv int, item int, quant int) returns void as $$
begin
  -- does this invoice + item combination already exist?
  perform 1 from lineitems
  where invoice_id = inv
  and item_id = item;
  if found then  -- yes? add this quantity
    update lineitems
    set quantity = quantity + quant
    where invoice_id = inv
    and item_id = item;
  else  -- no? insert
    insert into lineitems values (inv, item, quant);
  end if;
end;
$$ language plpgsql;
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/api01-1.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	Someone updates their cart, to &lt;strong&gt;change the quantity&lt;/strong&gt; of a Lineitem.
	If they change the quantity to 2, 5, or even 1, no problem, just &lt;strong&gt;update&lt;/strong&gt; the quantity.
	But what if they change the quantity to 0?
	You don’t want a Lineitem hanging around their cart with a quantity of 0.
	No, if the quantity is 0 or below, you want to &lt;strong&gt;delete&lt;/strong&gt; that Lineitem.
&lt;/p&gt;&lt;p&gt;
	So wrap all that logic in a simple &lt;strong&gt;function called cart_set&lt;/strong&gt;.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;-- update the quantity of an item in the cart
create function cart_set(inv int, item int, quant int) returns void as $$
begin
  if quant &amp;gt; 0 then
    update lineitems
    set quantity = quant
    where invoice_id = inv
    and item_id = item;
  else  -- quantity 0 or below? delete
    delete from lineitems
    where invoice_id = inv
    and item_id = item;
  end if;
end;
$$ language plpgsql;
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/api01-2.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	There, now this data logic is where it belongs: with the data itself.
&lt;/p&gt;&lt;p&gt;
	Your JavaScript, Python, Ruby, or whatever can just call the functions, like this:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;select cart_add(1, 1, 3);
select * from lineitems;

select cart_add(1, 2, 4);
select * from lineitems;

select cart_set(1, 2, 1);
select * from lineitems;
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/api01-3.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	Imagine if you did it like this for all of the important things you need to do in your database?
&lt;/p&gt;&lt;p&gt;
	Then any code, in any language, could just call those functions, knowing the database itself will handle the logic.
	Keeping the data-logic where it should be: with the data.
&lt;/p&gt;&lt;p&gt;
	Download the final example file here: &lt;a href=&quot;https://sive.rs/code/api01.sql&quot;&gt;/code/api01.sql&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	More on this in future posts.
	Or until then, see my &lt;a href=&quot;https://github.com/sivers/store&quot;&gt;example on Github&lt;/a&gt;.
&lt;/p&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/recalc</id>
	<title>Database trigger recalculates totals, for data integrity</title> 
	<published>2022-02-27T05:00:00Z</published>
	<updated>2022-02-27T05:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/recalc"/> 
	<summary type="text">When you make a database-backed app, you write code to ensure data integrity in related fields.</summary> 
	<content type="html">&lt;p&gt;
	When you make a database-backed app, you write code to ensure data integrity in related fields.
&lt;/p&gt;&lt;p&gt;
	If an item is sold, you update the inventory.
	If someone withdraws money, you update their balance.
	You recalcuate the total and update the related field in the database whenever somebody makes a change.
&lt;/p&gt;&lt;p&gt;
	Usually this is done in your main code: your JavaScript, Python, Ruby, Java, or whatever.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	But what if some future code doesn’t use your currently-crucial function?
&lt;/strong&gt;
	A new back-end interface, API hook, or simple shell script might not use your current code.
	New code will access the database directly, not using your old code.
&lt;/p&gt;&lt;p&gt;
	I encountered this exact situation at my last company, and felt the pain from inventory and even finances becoming wrong, all because some new code was accessing the database directly.
&lt;/p&gt;&lt;p&gt;
	So that’s why I’m an evangelist now for how important it is to &lt;strong&gt;put your crucial code in the database itself&lt;/strong&gt;.
	This is &lt;strong&gt;data logic&lt;/strong&gt; (not “business logic”) and &lt;strong&gt;should be bound to the data&lt;/strong&gt;.
	Database functions can be triggered, ensuring integrity, no matter what outside code is accessing it.
&lt;/p&gt;&lt;h3&gt;
	Here’s an example in PostgreSQL, using a shopping cart:
&lt;/h3&gt;&lt;p&gt;
	First, make three simple tables:
&lt;/p&gt;&lt;ol&gt;&lt;li&gt;
	Items with prices.
&lt;/li&gt;&lt;li&gt;
	Lineitems with quantities.
&lt;/li&gt;&lt;li&gt;
	Invoices with the total price.
&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;
	Create two example items, a $5 and a $9 item.
	And create invoice #1 for testing.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;create table items (
  id serial primary key,
  price int not null check (price &amp;gt; 0)
);

create table invoices (
  id serial primary key,
  total int
);

create table lineitems (
  invoice_id int not null references invoices(id),
  item_id int not null references items(id),
  quantity int not null check (quantity &amp;gt; 0),
  primary key (invoice_id, item_id)
);

-- example data:
insert into items (price) values (5);
insert into items (price) values (9);
insert into invoices (total) values (0);
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/recalc-1.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	Now, you want it to re-calculate the Invoice total whenever Lineitems are changed.
	That’s a special function called a &lt;strong&gt;trigger&lt;/strong&gt;.
	First you create the function, then create a trigger that executes the function.
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;-- re-calculate the total of a lineitem&amp;#39;s invoice
create function recalc() returns trigger as $$
begin
  -- update invoice using lineitems&amp;#39;s invoice_id
  update invoices set total = (
    select sum(quantity * price)
    from lineitems
    join items on lineitems.item_id = items.id
    where invoice_id = new.invoice_id)
  where id = new.invoice_id;
  return new;
end;
$$ language plpgsql;
-- run this function after any change to lineitems
create trigger recalc
  after insert or update or delete on lineitems
  for each row execute procedure recalc();
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/recalc-2.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	Test it by adding Lineitems, and updating their quantities.
	After each change, look at the Invoice.
	It works!
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;insert into lineitems (invoice_id, item_id, quantity) values (1, 1, 1);
insert into lineitems (invoice_id, item_id, quantity) values (1, 2, 1);
select * from lineitems; select * from invoices;

select &amp;#39;Notice new total when you update quantity:&amp;#39; look;
update lineitems set quantity = 5 where invoice_id = 1 and item_id = 2;
select * from lineitems; select * from invoices;

select &amp;#39;But when you delete a line? Oh no. Total is still $50&amp;#39; look;
delete from lineitems where invoice_id = 1 and item_id = 2;
select * from lineitems; select * from invoices;

-- Let&amp;#39;s try again in recalc-4.sql
drop function recalc() cascade;
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/recalc-3.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	But notice if you delete a Lineitem, it doesn’t update the Invoice!
	What the hell?
	Why not?
&lt;/p&gt;&lt;p&gt;
	Ah, that’s because the function refers to a record variable called “new”, passed by the trigger on Lineitems, and uses it to get the invoice_id.
	But when when you delete a Lineitem, the record variable is called “old”, since it refers to a Lineitem that’s already been deleted.
	Damn.
&lt;/p&gt;&lt;p&gt;
	You could make two separate triggers, one that uses “new”, called only on update and insert operations, and one that uses “old”, called only on deletes.
	But then you’d be duplicating the calculation of the total.
	So instead, just add one ugly if/then to assign either “new” or “old” to a variable called “r”, and use “r” instead.
	Here’s the updated function:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;-- re-calculate the total of a lineitem&amp;#39;s invoice
create function recalc() returns trigger as $$
declare
  r record;
begin
  -- use &amp;quot;new&amp;quot; lineitems record for insert/update, or &amp;quot;old&amp;quot; if delete
  if (tg_op = &amp;#39;DELETE&amp;#39;) then
    r = old;
  else
    r = new;
  end if;
  -- update invoice using lineitems(now &amp;quot;r&amp;quot;)&amp;#39;s invoice_id
  update invoices set total = (
    select sum(quantity * price)
    from lineitems
    join items on lineitems.item_id = items.id
    where invoice_id = r.invoice_id)
  where id = r.invoice_id;
  -- must return incoming &amp;quot;new&amp;quot; or &amp;quot;old&amp;quot; record when done
  return r;
end;
$$ language plpgsql;
-- run this function after any change to lineitems
create trigger recalc
  after insert or update or delete on lineitems
  for each row execute procedure recalc();
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/recalc-4.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	Now test adding, updating, and deleting, and notice they all update the Invoice total:
&lt;/p&gt;
&lt;div class=&quot;code&quot;&gt;
&lt;pre&gt;&lt;code&gt;insert into lineitems (invoice_id, item_id, quantity) values (1, 2, 1);
select * from lineitems; select * from invoices;

select &amp;#39;Notice new total when you update quantity:&amp;#39; look;
update lineitems set quantity = 5 where invoice_id = 1 and item_id = 2;
select * from lineitems; select * from invoices;

select &amp;#39;Notice new total when you delete:&amp;#39; look;
delete from lineitems where invoice_id = 1 and item_id = 2;
select * from lineitems; select * from invoices;
&lt;/code&gt;&lt;/pre&gt;&lt;small&gt;&lt;a href=&quot;https://sive.rs/code/recalc-5.sql&quot;&gt;download code&lt;/a&gt;&lt;/small&gt;
&lt;/div&gt;
&lt;p&gt;
	Cool?
	You get &lt;strong&gt;much more security and peace-of-mind&lt;/strong&gt;, knowing that no matter what code updates your database, the related fields will be updated automatically.
&lt;/p&gt;&lt;p&gt;
	Download the final example file here: &lt;a href=&quot;https://sive.rs/code/recalc.sql&quot;&gt;/code/recalc.sql&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	After you get this example working, let’s try another example of putting data logic in the database, by &lt;a href=&quot;https://sive.rs/api01&quot;&gt;making functions for updating the cart&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	Also look at &lt;a href=&quot;https://sive.rs/clean1&quot;&gt;using triggers to clean incoming data&lt;/a&gt;.
&lt;/p&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/pg2</id>
	<title>PostgreSQL example of self-contained stored procedures</title> 
	<published>2019-10-25T04:00:00Z</published>
	<updated>2019-10-25T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/pg2"/> 
	<summary type="text">First, see my previous article about PostgreSQL functions at sive.rs/pg.</summary> 
	<content type="html">&lt;p&gt;
	First, see &lt;a href=&quot;https://sive.rs/pg&quot;&gt;my previous article about PostgreSQL functions&lt;/a&gt; at &lt;a href=&quot;https://sive.rs/pg&quot;&gt;sive.rs/pg&lt;/a&gt;.
	That article gave tiny examples, but no finished working code.
&lt;/p&gt;&lt;p&gt;
	This week, I wrote a shopping cart to sell my books directly from my own site.
&lt;/p&gt;&lt;p&gt;
	So I took a couple extra hours today to put my code into public view, so anyone can play around with it.
	See &lt;a href=&quot;https://github.com/sivers/store&quot;&gt;github.com/sivers/store&lt;/a&gt;, to browse, download, and try it.
&lt;/p&gt;&lt;p&gt;
	It’s a working self-contained shopping cart / store.
	It’s a very concrete example of &lt;strong&gt;using stored procedures to keep all the data logic together&lt;/strong&gt; in one place.
	You can use it from JavaScript, Python, Ruby, or any language you want, since all the functionality is in the database itself.
	It works.
&lt;/p&gt;&lt;p&gt;
	If you have any questions, or want to tell me how &lt;a href=&quot;https://sive.rs/publicu&quot;&gt;stupid&lt;/a&gt; I am for doing this, &lt;a href=&quot;https://sive.rs/contact&quot;&gt;email me&lt;/a&gt;.
&lt;/p&gt;
&lt;img src=&quot;https://m.sive.rs/images/postgresql.png&quot; alt=&quot;postgresql logo&quot;&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/polut</id>
	<title>Digital pollution</title> 
	<published>2019-10-15T04:00:00Z</published>
	<updated>2019-10-15T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/polut"/> 
	<summary type="text">You couldn’t just roll down the street leaving huge piles of garbage everywhere you go, making life slower for everyone as they climb over your mountains of junk, just to get on with their life.</summary> 
	<content type="html">&lt;p&gt;
	You couldn’t just roll down the street leaving huge piles of garbage everywhere you go, making life slower for everyone as they climb over your mountains of junk, just to get on with their life.
	You’d feel bad about it, right?
&lt;/p&gt;&lt;p&gt;
	That’s how I feel about the digital things we put out into the world: websites, apps, and files.
&lt;/p&gt;&lt;p&gt;
	I prefer coding everything by hand, because I don’t like the huge piles of garbage that the automated generators create.
	These programs that generate a website, app, or file for you spit out thousands of lines of unnecessary junk when really only 10 lines are needed.
	Then people wonder why their site is so slow, and they think it’s their phone or connection’s fault.
&lt;/p&gt;&lt;p&gt;
	Yesterday I needed to make a little vector logo.
	Two lines and two triangles.
	I tried to use a couple different vector drawing programs but they saved it as hundreds of lines.
	I knew it could be simpler, so I read up on SVG and made exactly what I wanted:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&amp;lt;svg height=&amp;quot;54&amp;quot; width=&amp;quot;54&amp;quot;&amp;gt;
&amp;lt;defs&amp;gt;&amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;&amp;lt;![CDATA[line,polygon{stroke:black;stroke-width:4} polygon{fill:black}]]&amp;gt;&amp;lt;/style&amp;gt;&amp;lt;/defs&amp;gt;
&amp;lt;line x1=&amp;quot;2&amp;quot; y1=&amp;quot;2&amp;quot; x2=&amp;quot;2&amp;quot; y2=&amp;quot;52&amp;quot; /&amp;gt;
&amp;lt;line x1=&amp;quot;52&amp;quot; y1=&amp;quot;2&amp;quot; x2=&amp;quot;52&amp;quot; y2=&amp;quot;52&amp;quot; /&amp;gt;
&amp;lt;polygon points=&amp;quot;2,2 27,27 2,27&amp;quot; /&amp;gt;
&amp;lt;polygon points=&amp;quot;52,2 27,27 52,27&amp;quot; /&amp;gt;
&amp;lt;/svg&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&quot;https://m.sive.rs/images/HitMediaLogo-54.svg&quot; alt=&quot;Hit Media logo&quot;&gt;
&lt;p&gt;
	Much better!
	95% smaller file size, and the joy of making something by hand instead of having it done for me.
	But I think my biggest joy is &lt;strong&gt;eliminating the digital pollution&lt;/strong&gt; that the auto-generated one created.
	It makes everything faster, easier, and cleaner for anyone involved.
	95% less junk over the wires.
&lt;/p&gt;&lt;p&gt;
	Same thing with the EPUB file for my new book.
	Today I spent the day creating the EPUB’s XML and XHTML by hand, instead of using a generator.
	I love the manual control and again - 90% smaller file size.
&lt;/p&gt;&lt;p&gt;
	This makes me unreasonably happy.
	It feels like cleaning up the neighborhood.
	Or at least my yard.
&lt;/p&gt;&lt;p&gt;
	(And I love it when people notice how fast my site loads.)
&lt;/p&gt;
&lt;img src=&quot;https://m.sive.rs/images/HitMediaLogo-54.png&quot; alt=&quot;Hit Media logo&quot;&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/autom</id>
	<title>Where we do and don’t want automation</title> 
	<published>2019-10-10T04:00:00Z</published>
	<updated>2019-10-10T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/autom"/> 
	<summary type="text">I used to use Gmail.</summary> 
	<content type="html">&lt;p&gt;
	I used to use Gmail.
	But one day, as I typed my mother’s email address into the “To:” field, Google popped up a prompt asking if I also wanted to CC my uncle.
	That was so invasive and creepy that I deleted the account immediately and never used it again.
	I don’t want automated intelligence in my private email.
&lt;/p&gt;&lt;p&gt;
	My friend lives in a home full of the smartest technology, and loves getting all the new smart things, but he drives a deliberately retro old car with no computer chips.
	He loves to tinker with his car, and wants to do any maintenance himself.
&lt;/p&gt;&lt;p&gt;
	Another friend lives in a tech-free rustic cabin with no screens, but drives a Tesla.
&lt;/p&gt;&lt;p&gt;
	I do everything on a broken old Linux laptop, using only the command line, usually offline, nothing in the cloud.
	I think it’s because I don’t want any outside automation or intelligence in the work that matters to me.
&lt;/p&gt;&lt;p&gt;
	When software is described as “auto-”, “smart”, or “intelligent” it means that &lt;strong&gt;somebody else put their rules into it&lt;/strong&gt;.
	But I don’t want my computer to do anything I didn’t explicitly tell it to do.
	It shouldn’t change what I typed unless I tell it to.
	It should never guess or predict what I want.
&lt;strong&gt;
	I want full manual control.
&lt;/strong&gt;
&lt;/p&gt;&lt;p&gt;
	On the other hand, I don’t mind if my phone does these things, because I don’t care about my phone much.
	And I would love a high-tech car, full of smart AI automation, because I’m not a car aficionado.
&lt;/p&gt;&lt;p&gt;
	At first I thought that an expert at something won’t want assistance.
	But no, of course, auto-pilot for airline pilots, and IDEs for programmers.
&lt;/p&gt;&lt;p&gt;
	So I think it comes down to:
&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;
	how much of an expert you are at controlling this thing yourself
&lt;/li&gt;&lt;li&gt;
	how much you still enjoy doing it
&lt;/li&gt;&lt;li&gt;
	if you want the kind of assistance it provides
&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;
	Any thoughts on this subject?
	I’d love to hear another point of view.
&lt;/p&gt;
&lt;img src=&quot;https://m.sive.rs/images/robots.jpg&quot; alt=&quot;robots&quot;&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/pe</id>
	<title>Have a private email account</title> 
	<published>2019-09-28T04:00:00Z</published>
	<updated>2019-09-28T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/pe"/> 
	<summary type="text">I used to like the internet.</summary> 
	<content type="html">&lt;p&gt;
	I used to like the internet.
	I thought it was cool, creative, wild, untamed, expressive, decentralized, and educational.
	I guess it was, back then, but now?
	I kinda hate most of what’s out there.
&lt;/p&gt;&lt;p&gt;
	I don’t like social media, either.
	Staged photos, and people trying to get a reaction.
	Noise, hype, and drama.
	It makes me want to avoid the internet completely.
&lt;/p&gt;&lt;p&gt;
	Then I thought about what I do like.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	My email inbox is really nice.
&lt;/strong&gt;
	It’s only people I like, who are emailing me personally.
	No lists.
&lt;/p&gt;&lt;p&gt;
	I highly recommend setting up a private email address.
&lt;/p&gt;&lt;p&gt;
	Use &lt;a href=&quot;https://www.fastmail.com/&quot;&gt;Fastmail&lt;/a&gt;, &lt;a href=&quot;https://posteo.de/&quot;&gt;Posteo&lt;/a&gt;, &lt;a href=&quot;https://mailbox.org/en/&quot;&gt;Mailbox&lt;/a&gt;, or any similar service where you actually pay $1-$3 per month and in return get a completely ad-free spam-free wonderful email experience.
&lt;/p&gt;&lt;p&gt;
	Then never give this new private email to anyone except dear friends and family.
&lt;/p&gt;&lt;p&gt;
	Let your old Gmail collect the junk.
	The people you really care about will use the new one, so you won’t need to check the old one much anymore.
&lt;/p&gt;&lt;p&gt;
	It feels nice to have a notification mean something again — to only get one or two emails a day, and know that they are really for you.
	Or, if you don’t get any notifications, then nothing you really care about has arrived — so no need to check.
&lt;/p&gt;&lt;p&gt;
	Most days I don’t look at the web or any apps.
	I just write, text friends, call friends, and check email.
	That’s enough.
&lt;/p&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/openbsd</id>
	<title>OpenBSD : why and how</title> 
	<published>2018-04-20T04:00:00Z</published>
	<updated>2018-04-20T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/openbsd"/> 
	<summary type="text">The only operating system I use on my computers is not Mac, not Windows, and not even Linux.</summary> 
	<content type="html">&lt;p&gt;
	The only operating system I use on my computers is &lt;a href=&quot;https://sive.rs/itunes&quot;&gt;not Mac&lt;/a&gt;, not Windows, and not even Linux.
	It’s &lt;a href=&quot;https://www.openbsd.org/&quot;&gt;OpenBSD&lt;/a&gt;, and I love it so much.
	So I figured I should say a little something about why, and how you can try it.
&lt;/p&gt;
&lt;h3&gt;
	It’s probably not for you.
&lt;/h3&gt;
&lt;p&gt;
	It’s not for beginners.
	Most people should use &lt;a href=&quot;https://ubuntu.com/desktop&quot;&gt;Ubuntu&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	It’s not for people who want to click a button and have the computer hide the details from you.
&lt;/p&gt;&lt;p&gt;
	If software bloat doesn’t bother you — if every new Mac/Windows/Linux release you say, “Bring on the features! The more the better!” — it’s not for you.
&lt;/p&gt;&lt;p&gt;
	But if you’re experienced, like to “look under the hood”, and prefer software that does the minimum necessary, OpenBSD is for you.
&lt;/p&gt;
&lt;h3&gt;
	What is it?
&lt;/h3&gt;
&lt;p&gt;
	It’s like Linux, but has &lt;a href=&quot;https://www.openbsd.org/goals.html&quot;&gt;different goals&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	It’s known for its focus on security.
	But, like a well-engineered house will also be earthquake-proof, you don’t have to be paranoid about earthquakes to appreciate great construction.
	To me, the security features are just a side-effect of &lt;a href=&quot;https://www.openbsd.org/security.html&quot;&gt;great coding&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	OpenBSD comes with a secure minimal &lt;a href=&quot;https://www.nostarch.com/pf3&quot;&gt;firewall&lt;/a&gt;, &lt;a href=&quot;https://www.romanzolotarev.com/openbsd/httpd.html&quot;&gt;webserver&lt;/a&gt;, &lt;a href=&quot;https://www.opensmtpd.org/&quot;&gt;mailserver&lt;/a&gt;, and an optional graphical &lt;a href=&quot;http://www.xenocara.org/&quot;&gt;desktop&lt;/a&gt;.
	So if all you want is a few of those things, you do the default install, tweak one config file, and you’re done.
&lt;/p&gt;
&lt;h3&gt;
	Why OpenBSD instead of Linux?
&lt;/h3&gt;
&lt;p&gt;
	It’s &lt;strong&gt;uncompromising&lt;/strong&gt;.
	It’s not a people-pleaser or vendor-pleaser.
	Linux is in everything from Android phones to massive supercomputers, so has to include features for all of them.
	The OpenBSD developers say no to most things.
	Instead of trying to make it do more, they keep it focused on doing what it does with more security and reliability.
&lt;/p&gt;&lt;p&gt;
	They &lt;strong&gt;review and remove&lt;/strong&gt; code as often as they add.
	If something is unused, unmaintained, or unnecessary, they’ll axe it.
	If it’s unwieldy, they’ll make a &lt;strong&gt;small simple replacement&lt;/strong&gt;.
	For examples, see &lt;a href=&quot;https://man.openbsd.org/doas.1&quot;&gt;doas&lt;/a&gt;, &lt;a href=&quot;https://www.opensmtpd.org/&quot;&gt;OpenSMTPD&lt;/a&gt;, &lt;a href=&quot;https://man.openbsd.org/httpd.8&quot;&gt;httpd&lt;/a&gt;, and &lt;a href=&quot;https://www.libressl.org/&quot;&gt;LibreSSL&lt;/a&gt;.
	This is great for security, too.
	The more code, the more chance of a bug that could compromise your entire computer.
	The less code, the better.
	Each new release seems to be getting &lt;strong&gt;leaner&lt;/strong&gt; by removing old cruft.
	No other operating system does that.
&lt;/p&gt;&lt;p&gt;
	Great &lt;strong&gt;documentation&lt;/strong&gt; is a top priority.
	The built-in &lt;a href=&quot;https://en.wikipedia.org/wiki/Man_page&quot;&gt;man pages&lt;/a&gt; are amazing.
	So if you’re stuck on anything, &lt;a href=&quot;http://man.openbsd.org/apropos.1&quot;&gt;searching&lt;/a&gt; the man pages on your own computer is going to give you a better answer than searching Google.
	(This makes it nicer to work offline, too.)
&lt;/p&gt;&lt;p&gt;
	The &lt;strong&gt;installers&lt;/strong&gt; are amazing.
	The &lt;a href=&quot;http://www.openbsd.org/faq/faq4.html&quot;&gt;initial installation&lt;/a&gt; takes like five minutes.
	Hit [Enter] to the defaults, make your username and password, and it’s ready to go.
	Then the &lt;a href=&quot;http://www.openbsd.org/faq/faq15.html&quot;&gt;software installer&lt;/a&gt; is ideal, too.
	Just &lt;a href=&quot;https://man.openbsd.org/pkg_info.1&quot;&gt;pkg_info&lt;/a&gt; to search for something and &lt;a href=&quot;http://man.openbsd.org/pkg_add.1&quot;&gt;pkg_add&lt;/a&gt; to install it in seconds.
	(Which also installs all of its documentation, too.)
&lt;/p&gt;&lt;p&gt;
	Everything is rock-solid and &lt;strong&gt;just works&lt;/strong&gt;.
	Hardware I couldn’t get working in Linux just works on a first try with OpenBSD.
	And because they don’t stay cutting-edge, keeping a cautious pace, it keeps working and doesn’t break.
	The whole system is carefully planned and consistent, instead of a hodge-podge of bits and pieces.
&lt;/p&gt;&lt;p&gt;
	It’s all free and run by helpful volunteers.
	If you &lt;a href=&quot;http://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/&quot;&gt;searched ports&lt;/a&gt;, but some application you need is missing or out of date, just contact the maintainer and offer some assistance or money to help get it updated or added.
	I’ve donated $3850 to the developers to help improve the OpenBSD port of &lt;a href=&quot;https://nodejs.org/&quot;&gt;Node.js&lt;/a&gt;, &lt;a href=&quot;http://elixir-lang.org/&quot;&gt;Elixir&lt;/a&gt;, &lt;a href=&quot;http://www.erlang.org/&quot;&gt;Erlang&lt;/a&gt;, &lt;a href=&quot;https://apps.ankiweb.net/&quot;&gt;Anki&lt;/a&gt;, &lt;a href=&quot;http://www.ledger-cli.org/&quot;&gt;Ledger&lt;/a&gt;, and &lt;a href=&quot;http://www.qutebrowser.org/&quot;&gt;Qutebrowser&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	Also see &lt;a href=&quot;https://dataswamp.org/~solene/2022-06-22-openbsd-selling-arguments.html&quot;&gt;Solene’s list of selling points&lt;/a&gt;.
	I agree with those.
&lt;/p&gt;
&lt;img alt=&quot;&quot; src=&quot;https://m.sive.rs/images/openbsd.gif&quot;&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/netskill</id>
	<title>Use the internet, not just companies</title> 
	<published>2018-02-12T05:00:00Z</published>
	<updated>2018-02-12T05:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/netskill"/> 
	<summary type="text">I’ve been online since 1994, and seen so many companies come and go.</summary> 
	<content type="html">&lt;p&gt;
	I’ve been online since 1994, and seen so many companies come and go.
&lt;/p&gt;&lt;p&gt;
	In the year 2000, the place to be was mp3.com.
	Every musician would keep all of their music and fans there.
	A few years later, it was gone — shut down — all music and fan lists deleted.
&lt;/p&gt;&lt;p&gt;
	In 2005, it was MySpace.
	Again, musicians kept all of their music, photos, and fans there.
	A few years later, it was gone.
	Not shut down, but basically moot.
	There was no way to communicate with all of those people, because you didn’t have their direct contact info — you only had their MySpace inbox, which nobody checked anymore.
&lt;/p&gt;&lt;p&gt;
	As I’m writing this now in 2018, it’s Facebook, YouTube, and Spotify.
	Just like with mp3.com and MySpace, people act like these websites are everything, and keep all of their music, photos, and fans there.
	By the time you read this, they might be gone.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	Don’t depend on a company.
	They come and go.
&lt;/strong&gt;
	Think long-term.
	You’re going to be creating stuff, making fans, and building relationships for the rest of your life — much longer than these companies will last.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	So have your own website.
&lt;/strong&gt;
	Instead of sending your fans to some company’s site, send them to yours.
	Get everyone’s direct contact information so you don’t have to go through a company to reach them.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	Your website should be the definitive place to get everything you create.
&lt;/strong&gt;
	If you put your stuff on some company’s site, have it be secondary — a copy of the stuff that’s already on your site.
	That way you can use the popular networks without depending on them.
&lt;/p&gt;&lt;p&gt;
	Only rely on open standards that aren’t owned by any company — like email and the web.
&lt;/p&gt;
&lt;h2&gt;
	Email skills:
&lt;/h2&gt;
&lt;p&gt;
	Go into your email settings, and make sure you &lt;strong&gt;have a signature&lt;/strong&gt;.
	You need this because you’re going to be emailing people who have no idea who or where you are!
	Give them some context.
	Your signature should say who, what, and where, with a URL or two.
	For example:
&lt;/p&gt;
&lt;pre&gt;
--
Maya Danubé, fragrant jazz bass clarinet, New York City
http://mayadanube.com  me@mayadanube.com  (917)611-5310
Watch + listen: https://www.youtube.com/user/mayadanube
Friend me, baby: https://www.facebook.com/mayadanube
&lt;/pre&gt;
&lt;p&gt;
	When you email people, write a &lt;strong&gt;descriptive subject&lt;/strong&gt;.
	Never “hey” or “booking”.
	Try “Available June 6 for showcase?” or “introduction to photographer”.
	This is considerate.
	Now when your email is one of hundreds in an inbox, it will say exactly what is contained inside.
&lt;/p&gt;&lt;p&gt;
	Make it &lt;strong&gt;as short as possible&lt;/strong&gt;.
	The shorter your email, the more likely it will get a response.
	Be direct.
	Five sentences is ideal.
	If your email is too long, they are likely to procrastinate, and never get back to it.
&lt;/p&gt;&lt;p&gt;
	Use short paragraphs.
	Leave plenty of space.
	Reading a screen is different from reading a book.
&lt;/p&gt;
&lt;h2&gt;
	Web skills:
&lt;/h2&gt;
&lt;p&gt;
&lt;strong&gt;
	Know how to update your website.
&lt;/strong&gt;
	Don’t depend on someone else to do this for you.
	Know how to add new songs or videos, and how to make any changes.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	Know your URLs.
&lt;/strong&gt;
	Telling someone to go search for you is like telling them to look up your phone number.
	Instead, know your exact URLs (yoursite.com, twitter.com/something, facebook.com/whatever) so you can give it to people directly.
	If you don’t, they’ll probably never bother to go search for you.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	Know how to make an MP3.
&lt;/strong&gt;
	Give it a good filename like YOUR_NAME-Song_Title.mp3 (not mix7.mp3)
	Don’t use spaces in the filename.
	Edit the ID3 tags to put your full name and URL in the info, so whoever has this MP3 knows who it is and how to find you.
&lt;/p&gt;&lt;p&gt;
	Sorry if these sound too basic to you.
	But you’d be surprised by how many people don’t know these skills, and so are silently handicapped when interacting with the world.
&lt;/p&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/prog</id>
	<title>Should you learn programming? Yes.</title> 
	<published>2016-02-14T05:00:00Z</published>
	<updated>2016-02-14T05:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/prog"/> 
	<summary type="text">When I was 14 years old, my guitar teacher told me something important:</summary> 
	<content type="html">&lt;p&gt;
	When I was 14 years old, my guitar teacher told me something important:
&lt;/p&gt;&lt;p&gt;&lt;strong&gt;
	“You need to learn to sing. Because if you don’t, you’re always going to be at the mercy of some asshole singer.”
&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;
	His point was about self-reliance.
	If you ever have any ideas, whether for songs or apps, you need to have some basic skills to turn those ideas into reality.
&lt;/p&gt;&lt;p&gt;
	One of the most common things I hear from aspiring entrepreneurs is, &lt;strong&gt;“I have this idea for an app or site. But I’m not technical, so I need to find someone who can make it for me.”&lt;/strong&gt;
&lt;/p&gt;&lt;p&gt;
	I point them to my advice about &lt;a href=&quot;https://sive.rs/how2hire&quot;&gt;how to hire a programmer&lt;/a&gt;, but most programmers are already busy and expensive.
&lt;/p&gt;&lt;p&gt;
	Imagine if someone said, “I have this idea for a song.  But I’m not musical, so I need to find someone who will write, perform, and record it for me.”
&lt;/p&gt;&lt;p&gt;
	You’d probably advise them to just learn enough guitar or piano so they can play their song.
&lt;/p&gt;&lt;p&gt;&lt;strong&gt;
	It’s like learning to drive or make dinner.
	You only need to learn enough so you’re not helpless.
&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;
	So, yes, you should learn some programming.
	Basic HTML, CSS, and JavaScript are enough to start.
&lt;/p&gt;&lt;p&gt;&lt;strong&gt;
	I recommend &lt;a href=&quot;http://shop.oreilly.com/product/9780596159924.do&quot;&gt;Head First HTML and CSS&lt;/a&gt; first, then &lt;a href=&quot;http://shop.oreilly.com/product/0636920010906.do&quot;&gt;Head First HTML5 Programming&lt;/a&gt;.
&lt;/strong&gt;
	Those are very fun, visual books where you need to see the detailed illustrations, so get the paper book or PDF.
&lt;/p&gt;&lt;p&gt;
	If you prefer a course and community, use &lt;a href=&quot;https://www.freecodecamp.org/&quot;&gt;Free Code Camp&lt;/a&gt;.
	Ideally, do those books and this course at the same time, to really reinforce what you’re learning.
&lt;/p&gt;&lt;p&gt;
	You could go through those books or courses in a few weeks, and you’d already know as much as half of the people that call themselves web developers.
&lt;/p&gt;&lt;p&gt;
	It’s a really amazing feeling.
	The mystery is lifted.
	You’ll look at all websites in a new way.
	You’ll understand what’s going on behind the scenes.
	You’ll know how to do it yourself.
	It’s really empowering.
	(It’s definitely been the most rewarding thing I’ve ever learned.)
&lt;/p&gt;&lt;p&gt;
	For an inspiring example &lt;a href=&quot;http://blog.jenniferdewalt.com/post/56319597560/im-learning-to-code-by-building-180-websites-in&quot;&gt;see Jennifer Dewalt&lt;/a&gt;, who learned to code by building &lt;a href=&quot;http://blog.jenniferdewalt.com/post/62998082815/after-180-websites-im-ready-to-start-the-rest-of&quot;&gt;180 websites in 180 days&lt;/a&gt;, with no previous experience.
&lt;/p&gt;&lt;p&gt;
	Next, &lt;a href=&quot;https://sive.rs/learn-js&quot;&gt;read my advice on learning JavaScript&lt;/a&gt;.
&lt;/p&gt;
&lt;a href=&quot;http://www.flickr.com/photos/plarsen/35590387/&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;https://m.sive.rs/images/bicycle-repair.jpg&quot;&gt;&lt;/a&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/pg</id>
	<title>Simplify: move code into database functions</title> 
	<published>2015-05-04T04:00:00Z</published>
	<updated>2015-05-04T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/pg"/> 
	<summary type="text">If you are a web or API developer, programming code that uses an SQL database, this is for you.</summary> 
	<content type="html">&lt;p&gt;
	If you are a web or API developer, programming code that uses an SQL database, this is for you.
&lt;/p&gt;&lt;p&gt;
	I’ve found a very different and useful way to structure code.
	It’s made such a big difference for me that I had to share it here.
&lt;/p&gt;
&lt;h3&gt;
	How things are
&lt;/h3&gt;
&lt;p&gt;
	Most web development — whether custom or using frameworks like Rails, Django, Laravel, Sinatra, Flask, and Symfony — tends to work the same way:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
	At the core is a &lt;strong&gt;database&lt;/strong&gt;, which is just the storage of data.
&lt;/li&gt;&lt;li&gt;
	&lt;strong&gt;All intelligence&lt;/strong&gt; is in Ruby/Python/PHP/JavaScript classes.
&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;
	Why that’s bad
&lt;/h3&gt;
&lt;p&gt;
	These norms have some dangerous implications:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
	&lt;strong&gt;Everything&lt;/strong&gt; must go through these Ruby/Python/PHP/JavaScript classes — including shell scripts and other things not part of this application.
&lt;/li&gt;&lt;li&gt;
	&lt;strong&gt;Nothing&lt;/strong&gt; else may access the database directly, since doing so may break the rules defined by these surrounding classes.
&lt;/li&gt;&lt;li&gt;
	The &lt;strong&gt;database is treated as dumb storage&lt;/strong&gt;, even though the database is smart enough to have most of this logic built-in.
&lt;/li&gt;&lt;li&gt;
	But if you add data logic into the database itself, it’s now &lt;strong&gt;duplicated&lt;/strong&gt;, requiring changing in multiple places if the rules change.
&lt;/li&gt;&lt;li&gt;
	These two systems — the database and its surrounding code — are &lt;strong&gt;coupled and dependent&lt;/strong&gt; on each other.
&lt;/li&gt;&lt;li&gt;
	If it’s ever advantageous to &lt;strong&gt;switch applications&lt;/strong&gt; (say from a web app to mobile app, or Python to JavaScript), you’re going to have to &lt;strong&gt;re-write all of that data logic&lt;/strong&gt;.
&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;
	Simple vs complex
&lt;/h3&gt;
&lt;p&gt;
	Please go watch this amazing 35-minute talk as soon as possible:
&lt;strong&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=rI8tNMsozo0&quot;&gt;Simplicity Matters by Rich Hickey&lt;/a&gt;&lt;/strong&gt;.
&lt;/p&gt;&lt;p&gt;
	Here are his important points for this article:
&lt;/p&gt;&lt;ul&gt;&lt;li&gt;
	“&lt;strong&gt;Complex&lt;/strong&gt;” is objective.
	It means &lt;strong&gt;many things tied together&lt;/strong&gt;.
&lt;/li&gt;&lt;li&gt;
	“&lt;strong&gt;Simple&lt;/strong&gt;” is objective.
	It means &lt;strong&gt;one ingredient&lt;/strong&gt; — the opposite of complex.
&lt;/li&gt;&lt;li&gt;
	These are unrelated to “easy”.
	It is easy to install and bind yourself to something very complex (like &lt;a href=&quot;https://en.wikipedia.org/wiki/Object-relational_mapping&quot;&gt;ORM&lt;/a&gt;), and can be hard to build something simple.
&lt;/li&gt;&lt;li&gt;
	Classes, models, and methods (&lt;a href=&quot;https://en.wikipedia.org/wiki/Object-oriented_programming&quot;&gt;OOP&lt;/a&gt;) are an &lt;strong&gt;unnecessary complication&lt;/strong&gt;.
&lt;/li&gt;&lt;li&gt;
	Information is simple, so &lt;strong&gt;don’t hide it&lt;/strong&gt; behind a micro-language.
&lt;/li&gt;&lt;li&gt;
	&lt;strong&gt;Work with values directly&lt;/strong&gt;: hash/map of strings.
&lt;/li&gt;&lt;li&gt;
	Since a &lt;strong&gt;JSON API&lt;/strong&gt; — a hash/map of strings — is often the eventual interface, it’s even more reason to skip the abstractions and work with values directly.
&lt;/li&gt;&lt;/ul&gt;
&lt;h3&gt;
	Why this hit home for me
&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;
	Databases outlive the applications that access them.
&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;
	I’ve been using the same SQL database since 1997: same data, values, and SQL tables.
	But the code around it has changed so many times.
&lt;/p&gt;&lt;p&gt;
	In 1997, I started in Perl.
	In 1998, I switched to PHP.
	In 2004, a rewrite in Rails.
	In 2007, &lt;a href=&quot;https://sive.rs/rails2php&quot;&gt;back&lt;/a&gt; to PHP.
	In 2009, minimalist Ruby.
	In 2012, client-side JavaScript.
&lt;/p&gt;&lt;p&gt;
	&lt;strong&gt;Each time I’d have to re-write all of the logic around the database&lt;/strong&gt;:
	how to add a new person into the database,
	how to verify an invoice is correct,
	how to mark an order as paid, etc.
&lt;/p&gt;&lt;p&gt;
	But &lt;strong&gt;that whole time, my trusty PostgreSQL database stayed the same&lt;/strong&gt;.
&lt;/p&gt;&lt;p&gt;
	Since most of this is &lt;a href=&quot;https://rob.conery.io/2015/02/21/its-time-to-get-over-that-stored-procedure-aversion-you-have/&quot;&gt;data logic, not business logic&lt;/a&gt;, it should be in the database.
&lt;/p&gt;&lt;p&gt;
	So I’m putting this data logic directly into &lt;a href=&quot;https://www.postgresql.org/about/&quot;&gt;PostgreSQL&lt;/a&gt;, since I plan to stay with it for many more years, but plan to keep experimenting with programming languages.
(&lt;a href=&quot;https://nim-lang.org/&quot;&gt;Nim&lt;/a&gt;, &lt;a href=&quot;https://elixir-lang.org/&quot;&gt;Elixir&lt;/a&gt;, &lt;a href=&quot;https://racket-lang.org/&quot;&gt;Racket&lt;/a&gt;, &lt;a href=&quot;https://www.lua.org/about.html&quot;&gt;Lua&lt;/a&gt;, whatever.)
&lt;/p&gt;
&lt;h3&gt;
	How things could be
&lt;/h3&gt;
&lt;p&gt;
	Web developers have been treating the database as dumb storage, but it’s actually &lt;a href=&quot;https://www.postgresql.org/docs/current/server-programming.html&quot;&gt;quite smart&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	It’s &lt;strong&gt;simple&lt;/strong&gt; to have all of this intelligence &lt;strong&gt;in the database itself&lt;/strong&gt;.
&lt;/p&gt;&lt;p&gt;
	It’s &lt;strong&gt;complex&lt;/strong&gt; to have it tied to surrounding outside code.
&lt;/p&gt;&lt;p&gt;
	Once you put all of the intelligence directly into the database, then the outside code disappears!
&lt;/p&gt;&lt;p&gt;
	Then &lt;strong&gt;the database is self-contained, and not tied to anything&lt;/strong&gt;.
&lt;/p&gt;&lt;p&gt;
	Your outside interface can &lt;strong&gt;switch&lt;/strong&gt; to JavaScript, Haskell, Elixir or anything else with ease, because your core intelligence is all inside the database.
&lt;/p&gt;
&lt;h3&gt;
	How to do it
&lt;/h3&gt;
&lt;h4&gt;
	Table constraints
&lt;/h4&gt;
&lt;p&gt;
	The easiest place to start is &lt;a href=&quot;https://www.postgresql.org/docs/current/ddl-constraints.html&quot;&gt;constraints&lt;/a&gt;:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
create table people (
  id serial primary key,
  name text not null constraint no_name check (length(name) &gt; 0),
  email text unique constraint valid_email check (email ~ &#39;\A\S+@\S+\.\S+\Z&#39;)
);
create table tags (
  person_id integer not null references people(id) on delete cascade,
  tag varchar(16) constraint tag_format check (tag ~ &#39;\A[a-z0-9._-]+\Z&#39;)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
	Define what is considered valid/invalid data here.
&lt;/p&gt;&lt;p&gt;
	In my people example above, it says name can’t be empty, email must match that pattern with “@” and “.” and no whitespace.
	Then it says tags.person_id has to exist in the people table, but if the person is deleted then delete the tags, too.
	And the tag has to fit that regexp pattern of lowercase letters, numbers, dot, underscore, dash.
&lt;/p&gt;&lt;p&gt;
	It helps to name your constraints for later use in error catching.
&lt;/p&gt;
&lt;h4&gt;
	Triggers
&lt;/h4&gt;
&lt;p&gt;
	For things that happen before or after you alter data, use &lt;a href=&quot;https://www.postgresql.org/docs/current/trigger-definition.html&quot;&gt;triggers&lt;/a&gt;:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
create function clean() returns trigger as $$
begin
  new.name = btrim(regexp_replace(new.name, &#39;\s+&#39;, &#39; &#39;, &#39;g&#39;));
  new.email = lower(regexp_replace(new.email, &#39;\s&#39;, &#39;&#39;, &#39;g&#39;));
end;
$$ language plpgsql;
create trigger clean before insert or update of name, email on people
  for each row execute procedure clean();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
	This example cleans the input before it’s put into the database, in case someone accidentally put a space in their email address, or a line-break in their name.
&lt;/p&gt;
&lt;h4&gt;
	Functions
&lt;/h4&gt;
&lt;p&gt;
	Make little re-usable functions for things you’ll use often inside your code.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
create function get_person(a_name text, a_email text) returns setof people as $$
begin
  if not exists (select 1 from people where email = a_email) then
    return query insert into people (name, email)
      values (a_name, a_email) returning people.*;
  else
    return query select * from people where email = a_email;
  end if;
end;
$$ language plpgsql;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
	That’s one I use often:
	Given someone’s name and email, if they’re not already in my database, add them.
	Then, either way, return the database info for this person.
&lt;/p&gt;
&lt;h4&gt;
	Views for JSON
&lt;/h4&gt;
&lt;p&gt;
	Instead of requiring outside code to convert your data into JSON, you can have the &lt;a href=&quot;https://www.postgresql.org/docs/current/functions-json.html#FUNCTIONS-JSON-CREATION-TABLE&quot;&gt;database create JSON directly&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	For this, use &lt;a href=&quot;https://www.postgresql.org/docs/current/sql-createview.html&quot;&gt;views&lt;/a&gt; as JSON structure templates.
	Inside the view, use &lt;a href=&quot;https://www.postgresql.org/docs/current/functions-aggregate.html&quot;&gt;json_agg&lt;/a&gt; for nested values.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
create view person_view as
  select *, (select json_agg(t) as tags from
    (select tag from tags where person_id=people.id) t)
  from people;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
	This will be used in the API functions, below:
&lt;/p&gt;
&lt;h4&gt;
	API functions
&lt;/h4&gt;
&lt;p&gt;
	These are the only functions your outside code will access.
&lt;/p&gt;&lt;p&gt;
	They return only JSON.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
create function update_password(p_id integer, nu_pass text, out js json) as $$
begin
  update people set password=crypt(nu_pass, gen_salt(&#39;bf&#39;, 8)) where id = p_id;
  js := row_to_json(r) from (select * from person_view where id = p_id) r;
end;
$$ language plpgsql;
create function people_with_tag(a_tag text, out js json) as $$
begin
  js := json_agg(r) from
    (select * from person_view where id in
      (select person_id from tags where tag = a_tag)) r;
end;
$$ language plpgsql;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
	No matter what you need to do with your database, the &lt;a href=&quot;https://www.postgresql.org/docs/current/xplang.html&quot;&gt;procedural languages built-in to PostgreSQL&lt;/a&gt; can do it.
&lt;/p&gt;&lt;p&gt;
	&lt;a href=&quot;https://www.postgresql.org/docs/current/plpgsql-overview.html&quot;&gt;PL/pgSQL&lt;/a&gt; is &lt;strong&gt;not the most beautiful language&lt;/strong&gt;, but the &lt;strong&gt;simplicity&lt;/strong&gt; of having everything in the database is worth it.
&lt;/p&gt;&lt;p&gt;
	If you like JavaScript, check out the promising &lt;a href=&quot;https://plv8.github.io/&quot;&gt;plv8&lt;/a&gt;.
&lt;/p&gt;
&lt;h3&gt;
	Now, if you need a REST API:
&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;
require &#39;pg&#39;
require &#39;sinatra&#39;
DB = PG::Connection.new(dbconfig)
def qry(sql, params=[])
  @res = DB.exec_params(&#39;select js from &#39; + sql, params)
end
after do
  content_type &#39;application/json&#39;
  body @res[0][&#39;js&#39;]
end
get &#39;/people&#39; do
  qry(&#39;get_people()&#39;)
end
get %r{/people/([0-9]+)} do |id|
  qry(&#39;get_person($1)&#39;, [id])
end
put %r{/people/([0-9]+)} do |id|
  qry(&#39;update_password($1, $2)&#39;, [id, params[:password]])
end
get &#39;/people/tagged&#39; do
  qry(&#39;people_with_tag($1)&#39;, [params[:tag]])
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;
	Or if you need a client library:
&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;
require &#39;pg&#39;
require &#39;json&#39;
DB = PG::Connection.new(dbconfig)
def js(func, params=[])
  res = DB.exec_params(&#39;select js from &#39; + func, params)
  JSON.parse(res[0][&#39;js&#39;])
end
def people
  js(&#39;get_people()&#39;)
end
def person(id)
  js(&#39;get_person($1)&#39;, [id])
end
def update_password(id, newpass)
  js(&#39;update_password($1, $2)&#39;, [id, newpass])
end
def people_tagged(tag)
  js(&#39;people_with_tag($1)&#39;, [tag])
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;
	That’s it!
&lt;/h3&gt;
&lt;p&gt;
	Now whether a REST API or client library, &lt;strong&gt;all it really has to do is pass the arguments into the database functions, and return the JSON&lt;/strong&gt;.
&lt;/p&gt;&lt;p&gt;
	I’m not trying to convince everyone to do things this way.
	But I hope you find it useful or at least interesting to consider.
&lt;/p&gt;
&lt;img src=&quot;https://m.sive.rs/images/postgresql.png&quot; alt=&quot;postgresql logo&quot;&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/srs</id>
	<title>Memorizing a programming language using spaced repetition software</title> 
	<published>2013-01-06T05:00:00Z</published>
	<updated>2013-01-06T05:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/srs"/> 
	<summary type="text">I’ve been doing this for a year, and it’s the most helpful learning technique I’ve found in 14 years of computer programming.</summary> 
	<content type="html">&lt;p&gt;
	I’ve been doing this for a year, and it’s &lt;strong&gt;the most helpful learning technique I’ve found in 14 years of computer programming&lt;/strong&gt;.
&lt;/p&gt;
&lt;h3&gt;
	Background:
&lt;/h3&gt;
&lt;p&gt;
	I’m an intermediate programmer.
	I didn’t go to school for it.
	I just learned by necessity because I started a website that just kept growing and growing, and I couldn’t afford to hire a programmer, so I picked up a few books on PHP, SQL, Linux, and Apache, learned just enough to make it work, then used that little knowledge for years.
&lt;/p&gt;&lt;p&gt;
	But later, when I worked along side a real programmer, I was blown away by his vocabulary!
	All of these commands and functions just flowing effortlessly out of his fingers.
	We were using the same language, but &lt;strong&gt;he had memorized so much of it&lt;/strong&gt;, that I felt like a child next to a university professor.
	I really wanted to get that kind of fluency.
&lt;/p&gt;&lt;p&gt;
	It made me think about how much I’ve &lt;strong&gt;learned then immediately forgotten&lt;/strong&gt;, over the years.
	I read books or articles about some useful feature, try it once, but then I get distracted, forget about it, and go about my normal way of doing things.
&lt;/p&gt;&lt;p&gt;
	I wanted to deeply memorize the commands and techniques of the language, and not forget them, so that they stay at the forefront of my mind whenever I need them.
&lt;/p&gt;
&lt;h3&gt;
	Spaced Repetition:
&lt;/h3&gt;
&lt;p&gt;
	When you hear a new fact, it’s forgotten pretty quickly unless it’s brought back to the forefront of your mind repeatedly.
&lt;/p&gt;&lt;p&gt;
	You can do this haphazardly by immersing yourself in a language, for example, where the new words you learn will be brought up by chance occasionally.
&lt;/p&gt;&lt;p&gt;
	But memory research shows that &lt;strong&gt;the most effective and efficient time for a new fact to be remembered is right before you were about to forget it&lt;/strong&gt;.
&lt;/p&gt;&lt;p&gt;
&lt;a href=&quot;http://quantifiedself.com/2012/06/spaced-repetition-and-learning/&quot;&gt;&lt;img src=&quot;https://m.sive.rs/images/forgetting-curve-srs.jpg&quot; alt=&quot;forgetting curve&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;&lt;p&gt;
	Say if you learn a new word in a foreign language, you’d want to practice it again a few minutes after hearing it, then a few hours, then the next day, then in 2 days, then 5 days, then 10 days, 3 weeks, 6 weeks, 3 months, 8 months, etc.
	After a while it’s basically permanently memorized with a rare reminder.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	Spaced Repetition Software does this for you&lt;/strong&gt;, so you can just &lt;strong&gt;give it a bunch of facts you want to remember, then have it quiz you once a day, and it manages the intervals based on your feedback.&lt;/strong&gt;
	After each quiz question, if you say that one was easy, it won’t be introduced for a long time, but if you were stumped, then it’ll ask it again in a few minutes, until you’ve got it.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	Go to &lt;a href=&quot;https://apps.ankiweb.net/&quot;&gt;apps.ankiweb.net&lt;/a&gt; and download Anki&lt;/strong&gt;.
	It’s a free, open source, popular spaced repetition software program.
&lt;/p&gt;&lt;p&gt;
	As for programming, you get where I’m going with this.
&lt;/p&gt;&lt;p&gt;
	What if you could &lt;strong&gt;memorize everything about the programming language&lt;/strong&gt; of your choice?
	Every command, every parameter, every function.
	Every solution to hundreds of the most common problems, all completely memorized at your fingertips?
	Imagine going through the documentation or a book, and &lt;strong&gt;permanently remembering every single thing in it?&lt;/strong&gt;
&lt;/p&gt;&lt;p&gt;
	Enough of the intro, let’s get to the HOW-TO:
&lt;/p&gt;
&lt;h3&gt;
	First, learn!
&lt;/h3&gt;
&lt;p&gt;
&lt;strong&gt;
	Flash cards are for &lt;em&gt;remembering&lt;/em&gt; what you’ve learned.
&lt;/strong&gt;
&lt;/p&gt;&lt;p&gt;
	Before you create a flash card for something, you need to actually &lt;strong&gt;learn and understand it.&lt;/strong&gt;
	Create the flash card after you’ve really understood.
&lt;/p&gt;&lt;p&gt;
	(This is why it’s not that helpful to look at someone else’s deck.
	Those are just reminders.)
&lt;/p&gt;
&lt;h3&gt;
	Convert Knowledge into Small Facts:
&lt;/h3&gt;
&lt;p&gt;
	You’re going to be making a bunch of flash cards.
	Question on the front.
	Answer on the back.
&lt;/p&gt;&lt;p&gt;
	If you were just using this to memorize foreign language vocabulary, then the formatting would be easy.
	The front would have a word or phrase, and the back would have its translation, and vice-versa.
&lt;/p&gt;&lt;p&gt;
&lt;img src=&quot;https://m.sive.rs/images/anki-cn-1.png&quot; alt=&quot;example chinese flash card question&quot; /&gt;&lt;br/&gt;
&lt;img src=&quot;https://m.sive.rs/images/anki-cn-2.png&quot; alt=&quot;example chinese flash card answer&quot; /&gt;
&lt;/p&gt;&lt;p&gt;
&lt;img src=&quot;https://m.sive.rs/images/anki-cn-3.png&quot; alt=&quot;example chinese flash card question&quot; /&gt;&lt;br/&gt;
&lt;img src=&quot;https://m.sive.rs/images/anki-cn-4.png&quot; alt=&quot;example chinese flash card answer&quot; /&gt;
&lt;/p&gt;&lt;p&gt;
	But if you’re learning anything else, you’re going to have to put a little craft and creativity into making your own flash cards.
&lt;/p&gt;&lt;p&gt;
	It takes some effort to read through paragraphs of stuff you want to remember, pick out the key facts, break them down into their smallest form, and turn them into questions for quizzing your future self.
&lt;/p&gt;&lt;p&gt;
	Here are my best time-saving tips from a year of doing this:
&lt;/p&gt;
&lt;h4&gt;
	Turn prose into code
&lt;/h4&gt;
&lt;p&gt;
	If you’re reading a tutorial about programming, and come across a paragraph describing a feature.
&lt;/p&gt;
&lt;blockquote&gt;“The add (+) operator... if only one operand is a string, the other operand is converted to a string and the result is the concatenation of the two strings.”&lt;/blockquote&gt;
&lt;p&gt;
	You test it out yourself, play around with it, and understand it.
	So you make a flashcard to remember it.
&lt;/p&gt;
&lt;pre class=&quot;anki1&quot;&gt;
  var a = 5 + &#39;5&#39;;
  // what is a?
&lt;/pre&gt;&lt;pre class=&quot;anki2&quot;&gt;
  &#39;55&#39;
  If either side of + is a string, the other is
  converted to a string before adding like strings.
&lt;/pre&gt;
&lt;h4&gt;
	Try to trick your future self
&lt;/h4&gt;
&lt;p&gt;
	Sometimes you learn a “gotcha” — a common mistake or surprising feature.
&lt;/p&gt;
&lt;blockquote&gt;“If the new Array constructor is passed a single number, it creates an empty Array with a length of that number. Any other combination of arguments creates an Array of those arguments.”&lt;/blockquote&gt;
&lt;p&gt;
	You test it out yourself, play around with it, and understand it.
	Then make two flash cards to try to trick your future self.
&lt;/p&gt;
&lt;pre class=&quot;anki1&quot;&gt;
  var a = new Array(&#39;5&#39;);
  // what is a?
&lt;/pre&gt;&lt;pre class=&quot;anki2&quot;&gt;
  An array with one item, the string &#39;5&#39;: [&#39;5&#39;];
&lt;/pre&gt;
  ... and then an almost-identical question ...
&lt;pre class=&quot;anki1&quot;&gt;
  var a = new Array(5);
  // what is a?
&lt;/pre&gt;&lt;pre class=&quot;anki2&quot;&gt;
  An empty array with a length of 5.
&lt;/pre&gt;
&lt;p&gt;
	When the program quizzes you, it will shuffle the cards, so that hopefully your examples will intentionally catch you by surprise.
&lt;/p&gt;&lt;p&gt;
	You can also try to trick yourself with more complicated examples, to keep these gotchas fresh in your mind:
&lt;/p&gt;
&lt;pre class=&quot;anki1&quot;&gt;
  var a = [20, 10, 5, 1];
  // what is a.sort()?
&lt;/pre&gt;&lt;pre class=&quot;anki2&quot;&gt;
  [1, 10, 20, 5]
  // sort treats all values as strings
&lt;/pre&gt;
&lt;p&gt;
	Don’t forget to &lt;strong&gt;quiz yourself on the solution, too:&lt;/strong&gt;
&lt;pre class=&quot;anki1&quot;&gt;
  var a = [20, 10, 5, 1];
  // sort these in numeric order
&lt;/pre&gt;&lt;pre class=&quot;anki2&quot;&gt;
  function compare(v1, v2) { return(v1 — v2); }
  a.sort(compare);
&lt;/pre&gt;
&lt;h4&gt;
	Save the cool tricks
&lt;/h4&gt;
&lt;p&gt;
	If you find a cool trick you want to remember, turn it into the answer of a small challenge.
&lt;/p&gt;
&lt;pre class=&quot;anki1&quot;&gt;
  var albums = [
    {name: &#39;Beatles&#39;, title: &#39;White Album&#39;, price: 15},
    {name: &#39;Zeppelin&#39;, title: &#39;II&#39;, price: 7}];
  // make this work:
  albums.sort(by(&#39;name&#39;));
  albums.sort(by(&#39;title&#39;));
  albums.sort(by(&#39;price&#39;));
&lt;/pre&gt;&lt;pre class=&quot;anki2&quot;&gt;
  function by(propName) {
    return function(obj1, obj2) {
      v1 = obj1[propName];
      v2 = obj2[propName];
      if (v1 &lt; v2) { return -1; }
      else if (v1 &gt; v2) { return 1; }
      else { return 0; }
    };
  }
&lt;/pre&gt;
&lt;h4&gt;
	Make the answer require multiple solutions
&lt;/h4&gt;
&lt;p&gt;
	If there’s more than one way of doing something, and you want to remember both, make your future self come up with more than one solution, so you can keep both alternatives in mind.
&lt;/p&gt;
&lt;pre class=&quot;anki1&quot;&gt;
  s = &#39;string like this&#39;
  # In Ruby, show two ways to turn it into &#39;String Like This&#39;
&lt;/pre&gt;&lt;pre class=&quot;anki2&quot;&gt;
  s.split.map(&amp;amp;:capitalize).join(&#39; &#39;)
  s.gsub(/\b\S/) {|x| x.upcase}
&lt;/pre&gt;
&lt;h4&gt;
	Turn broad concepts into succinct examples
&lt;/h4&gt;
&lt;p&gt;
	Say you just spent 20 minutes learning something that’s more conceptual, and not as much about remembering specific functions.
	Sometimes all you need is one succinct example to remind yourself of the concept.
&lt;/p&gt;
&lt;pre class=&quot;anki1&quot;&gt;
  /(a(b)((c)d))/.match(&#39;abcd&#39;)
  # What will $1, $2, $3, $4 be?
&lt;/pre&gt;&lt;pre class=&quot;anki2&quot;&gt;
  $1 = &#39;abcd&#39;
  $2 = &#39;b&#39;
  $3 = &#39;cd&#39;
  $4 = &#39;c&#39;
&lt;/pre&gt;
	Another example:
&lt;pre class=&quot;anki1&quot;&gt;
  class C
    self
  end
  class D &lt; C
  end
  d1 = D.new
  # which object is self?
&lt;/pre&gt;&lt;pre class=&quot;anki2&quot;&gt;
  class D
&lt;/pre&gt;
&lt;h4&gt;
	Read “&lt;a href=&quot;https://www.supermemo.com/en/blog/twenty-rules-of-formulating-knowledge&quot;&gt;20 Rules of Formulating Knowledge&lt;/a&gt;” by Piotr Wozniak
&lt;/h4&gt;
&lt;p&gt;
	The best advice on this stuff is an article called “&lt;a href=&quot;https://www.supermemo.com/en/blog/twenty-rules-of-formulating-knowledge&quot;&gt;20 Rules of Formulating Knowledge&lt;/a&gt;” by Piotr Wozniak.
	So please read that one.
&lt;/p&gt;
&lt;h3&gt;
	Run Through it Daily
&lt;/h3&gt;
&lt;p&gt;
	For most efficient results, turn on your spaced repetition software once a day.
	If you go too long without, you’ll screw up all the timings, and have to re-learn stuff you would have remembered.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	You can remember thousands of these facts in only 20 minutes a day.
&lt;/strong&gt;
	I just make it a morning routine.
	Make a cup of boiling tea.
	Do my Anki.
	Drink my tea.
&lt;/p&gt;&lt;p&gt;
	It’s fun when quizzing yourself to add a little adrenaline, and make yourself go as fast as you can.
&lt;/p&gt;&lt;p&gt;
	It’s like a mental visit to the gym.
	A little intense 20 minutes a day is so worth it for the immediate and long-term results.
&lt;/p&gt;&lt;p&gt;
	Add some new cards whenever you can, and you’ll be amazed that everything you saved stays fresh in your mind.
&lt;/p&gt;
&lt;h3&gt;
	Conclusion
&lt;/h3&gt;
&lt;p&gt;
	I’ve been doing this for a year, and it’s been a HUGE boost to my fluency.
	I highly recommend it, as you can tell.
&lt;/p&gt;&lt;p&gt;
	Feel free to &lt;a href=&quot;https://sive.rs/contact&quot;&gt;email me anytime&lt;/a&gt; to let me know how it goes for you.
&lt;/p&gt;
&lt;h3&gt;
	Read more:
&lt;/h3&gt;
&lt;p&gt;
	The &lt;a href=&quot;https://duckduckgo.com/?q=Janki+Method+Refined&quot;&gt;“Janki Method Refined” by Jack Kinsella&lt;/a&gt; is a great article on similar approach,and explains it even better than I did here.
&lt;/p&gt;&lt;p&gt;
	You can also use Anki to learn &lt;a href=&quot;https://quantifiedself.com/blog/spaced-repetition-and-learning/&quot;&gt;all kinds of things&lt;/a&gt;.
&lt;/p&gt;&lt;p&gt;
	&lt;a href=&quot;https://augmentingcognition.com/ltm.html&quot;&gt;“Augmenting Long-term Memory” by Michael Nielsen&lt;/a&gt; is an amazing article about using Anki for learning anything.
&lt;/p&gt;&lt;p&gt;
	Read &lt;a href=&quot;https://www.wired.com/2008/04/ff-wozniak/&quot;&gt;the interview with Piotr Wozniak at wired.com&lt;/a&gt; for a story about a guy taking this to the limit.
&lt;/p&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/emailer</id>
	<title>How to send personalized emails</title> 
	<published>2009-08-31T04:00:00Z</published>
	<updated>2009-08-31T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/emailer"/> 
	<summary type="text">People often ask what I use to send out my personalized emails.</summary> 
	<content type="html">&lt;p&gt;
	People often ask what I use to send out my personalized emails.
&lt;/p&gt;&lt;p&gt;
	It’s &lt;strong&gt;a short little PHP script, run on the command-line, directly on your webserver&lt;/strong&gt;.
	The script and simple instructions are below.
&lt;/p&gt;
&lt;h3&gt;
	1. Keep your list in a spreadsheet
&lt;/h3&gt;
&lt;p&gt;
	It’s important that you do it with &lt;strong&gt;email address in column A&lt;/strong&gt; and &lt;strong&gt;name in column B&lt;/strong&gt; like this:
&lt;br /&gt;
&lt;img alt=&quot;&quot; src=&quot;https://m.sive.rs/images/wonkakids.png&quot;&gt;
&lt;/p&gt;
&lt;h3&gt;
	2. Save as tab-delimited text
&lt;/h3&gt;
&lt;p&gt;
	Under “Save As...”, every spreadsheet program has an option to save as plain text, with the columns separated with a “tab” character.
	For this example, &lt;strong&gt;name it list.txt&lt;/strong&gt;.
	Upload it to your server.
&lt;/p&gt;
&lt;h3&gt;
	3. Here’s the PHP script that does it
&lt;/h3&gt;
&lt;pre&gt;
&amp;lt;?php
$from = &amp;quot;Willy Wonka &amp;lt;willy@wonka.com&amp;gt;&amp;quot;;
foreach(file(’list.txt’) as $line)
    {
    list($email, $name) = explode(&amp;quot;\t&amp;quot;, $line);
    list($firstname) = explode(’ ’, $name);
    $subject = &amp;quot;Hi $firstname! The chocolate is all yours.&amp;quot;;
    $body = &amp;quot;Hi $firstname — 
Those other kids are awful. You deserve it all. Come and get it.
--
Willy Wonka — willy@wonka.com — http://wonka.com
&amp;quot;;
    mail($email, $subject, $body, $from);
    print &amp;quot;$email sent\n&amp;quot;;
    }
?&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
	&lt;strong&gt;Look at it slowly&lt;/strong&gt;, even if you don’t know PHP.
	It’s pretty self-explanatory.
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
	It goes through each line of your list.txt file
&lt;/li&gt;
&lt;li&gt;
	Breaks each line into $email and $name, separated by tab (\t)
&lt;/li&gt;
&lt;li&gt;
	Breaks the name into words, taking the first one as $firstname
&lt;/li&gt;
&lt;li&gt;
	Merges $firstname into the subject and body of the mail
&lt;/li&gt;
&lt;li&gt;
	Emails it, using the customized $email, $subject and $body
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
	Obviously, &lt;strong&gt;replace the $from, $subject, and $body&lt;/strong&gt; for your own needs.
	Save it as &lt;strong&gt;mailer.php&lt;/strong&gt;.
	Then upload it to your server.
&lt;/p&gt;
&lt;h3&gt;
	4. Log in to the command-line on your server
&lt;/h3&gt;
&lt;p&gt;
	This is the part your web-hosting company will have to tell you how to do.
	Using Terminal on Mac, or PuTTY on Windows, SSH into your server’s command-line.
&lt;/p&gt;
&lt;h3&gt;
	5. Run it!
&lt;/h3&gt;
&lt;p&gt;
	On the command-line, where the &lt;strong&gt;mailer.php&lt;/strong&gt; and &lt;strong&gt;list.txt&lt;/strong&gt; files are, just type…
&lt;/p&gt;
&lt;pre&gt;php mailer.php&lt;/pre&gt;
&lt;p&gt;
	…and everyone will be sent a customized email.
&lt;/p&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/kidspc</id>
	<title>A computer that would require kids to be creative to use it</title> 
	<published>2009-07-26T04:00:00Z</published>
	<updated>2009-07-26T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/kidspc"/> 
	<summary type="text">Talking with a parent yesterday about kids’ use of computers.</summary> 
	<content type="html">&lt;p&gt;
	Talking with a parent yesterday about kids’ use of computers.
	She felt that her kids were still too passive with computers, just turning it on and playing games, surfing, or chatting — but not really creating or learning.
&lt;/p&gt;&lt;p&gt;
	I had an idea for a computer that would require kids to be creative to use it:
&lt;/p&gt;&lt;p&gt;
	What if there was &lt;strong&gt;a computer that did nothing by default, but had all the building blocks to make it do anything, with a little effort?&lt;/strong&gt;
&lt;/p&gt;&lt;p&gt;
	Say you turn it on, and there are just &lt;strong&gt;some puzzle pieces on the screen, and some “HOW TO” instructions.&lt;/strong&gt;
&lt;/p&gt;&lt;p&gt;
	Want to browse the web?
	You need to connect the internet-connection block, HTML parsing block, and user interface block.
	Bundle them up, type a command to name it anything you want, give it an icon, and now you have a web browser program on your desktop forever... and you made it!
&lt;/p&gt;&lt;p&gt;
	Want to chat with friends?
	You need to connect the internet-connection block, message-sending block, message-receiving block, smiley-face-making block, and user interface block.
	Bundle it up, type a command to name it anything you want, give it an icon, and now you have a chat program.
&lt;/p&gt;&lt;p&gt;
	Maybe you could go back and add more blocks to your chat program, like a video-camera interface or file-sending interface.
&lt;/p&gt;&lt;p&gt;
	All of this could be as easy as dragging puzzle pieces together, but each piece could have options for customizing, though &lt;strong&gt;the best options would only be available by editing the (easy) code.&lt;/strong&gt;
	This would get kids used to the idea of programming, and seeing real results.
&lt;/p&gt;&lt;p&gt;
	My first computer was a &lt;a href=&quot;https://en.wikipedia.org/wiki/TRS-80&quot;&gt;Radio Shack TRS-80&lt;/a&gt; in 1978.
	When you powered it up, it just gave you a black screen and a command prompt.
	It was up to you to learn how to type commands to make it do what you wanted.
	That sense of control and creativity would be useful again for kids.
&lt;/p&gt;
&lt;img alt=&quot;&quot; src=&quot;https://m.sive.rs/images/puzzlepieces.jpg&quot;&gt;</content>
</entry> 
<entry>
	<id>https://sive.rs/rails2php</id>
	<title>7 reasons I switched back to PHP after 2 years on Rails</title> 
	<published>2007-09-23T04:00:00Z</published>
	<updated>2007-09-23T04:00:00Z</updated>
	<link rel="alternate" type="text/html" href="https://sive.rs/rails2php"/> 
	<summary type="text">This was originally posted on O’Reilly in September 2007.</summary> 
	<content type="html">&lt;p&gt;&lt;em&gt;
	This was originally posted on &lt;a href=&quot;http://archive.oreilly.com/pub/post/7_reasons_i_switched_back_to_p_1.html&quot;&gt;O’Reilly&lt;/a&gt; in September 2007.
	Re-posting it here for a permanent archive.
&lt;/em&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;
	SUMMARY:
&lt;strong&gt;
	I spent two years trying to make Rails do something it wasn’t meant to do, then realized my old abandoned language would do just fine if approached with my new Rails-gained wisdom.
&lt;/strong&gt;
&lt;/p&gt;&lt;p&gt;
	BACKGROUND:
&lt;/p&gt;&lt;p&gt;
	Back in January 2005, I announced that I was going to completely scrap over 100,000 lines of messy PHP code in my existing CD Baby (cdbaby.com) website, and rewrite the entire thing in Rails, from scratch.
&lt;/p&gt;&lt;p&gt;
	I hired one of the best Rails programmers in the world (Jeremy Kemper), and we set off on this huge task with intensity.
	The first few months showed good progress, and Jeremy could not have been more amazing, twisting the deep inner guts of Rails to make it do things it was never intended to do.
&lt;/p&gt;&lt;p&gt;
	But at every step, it seemed our needs clashed with Rails’ preferences.
	Like trying to turn a train into a boat.
	It’s possible with a lot of glue.
	But it’s damn hard, and makes you ask why you’re really doing this.
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	Two years later, after various setbacks, we were less than halfway done.
&lt;/strong&gt;
	(To be fair to Jeremy’s expertise: many setbacks were because of tech emergencies that pulled our attention to other internal projects that were not the rewrite itself.)
	The entire music distribution world had changed, and we were still working on the same goddamn rewrite.
	I said fuckit, and we abandoned the Rails rewrite.
	Jeremy took a job with &lt;a href=&quot;https://basecamp.com/&quot;&gt;37 Signals&lt;/a&gt;, and that was that.
&lt;/p&gt;&lt;p&gt;
	I didn’t abandon the rewrite idea, though.
	I just asked myself one important question:
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;
	“Is there anything Rails can do, that PHP can’t do?”
&lt;/strong&gt;
&lt;/p&gt;&lt;p&gt;
	The answer is no.
&lt;/p&gt;&lt;p&gt;
	I threw away 2 years of Rails code, and opened a new empty project.
&lt;/p&gt;&lt;p&gt;
	Then in a mere two months, by myself, not even telling anyone I was doing this, using nothing but vim, and no frameworks, I rewrote CD Baby from scratch in PHP.
	Done!
	Launched!
	And it works amazingly well.
&lt;/p&gt;&lt;p&gt;
	It’s the most beautiful PHP I’ve ever written, all wonderfully &lt;a href=&quot;https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&quot;&gt;MVC&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Don&#39;t_repeat_yourself&quot;&gt;DRY&lt;/a&gt;, and I owe it all to Rails.
&lt;/p&gt;
&lt;h3&gt;
	Inspired by Rails:
&lt;/h3&gt;
&lt;ul&gt;&lt;li&gt;
	all logic is coming from the models, one per database table, like Martin Fowler’s Active Record pattern.
&lt;/li&gt;&lt;li&gt;
	no requires or includes needed, thanks to __autoload
&lt;/li&gt;&lt;li&gt;
	real MVC separation: controllers have no HTML or business-logic, and only use REST-approved HTTP.
&lt;/li&gt;&lt;li&gt;
	all HTML coming from a cute and powerful templating system I whipped up in 80 lines, all multi-lingual and caching and everything
&lt;/li&gt;&lt;li&gt;
	… and much more. In only 12,000 lines of code, including HTML templates. (Down from 90,000, before.)
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;
	Though I’m not saying other people should do what I’ve done, I thought I should share my reasons and lessons-learned, here:
&lt;/p&gt;
&lt;h3&gt;
	7 reasons I switched back to PHP after 2 years on Rails:
&lt;/h3&gt;
&lt;h4&gt;
	#1 — “Is there anything Rails/Ruby can do that PHP can’t do? No.”
&lt;/h4&gt;
&lt;p&gt;
	For 2 years, I thought Rails is genius, PHP is shit. Rails is powerful, PHP is crap.
	I was nearly killing my company in the name of blindly insisting Rails was the answer to all questions, timeframes be damned.
	But when I took a real emotionless non-prejudiced look at it, I realized the language didn’t matter that much.
	Ruby is prettier. Rails has nice shortcuts. But no big shortcuts I can’t code-up myself in a day if needed.
	Looked at from a real practical point of view, I could do anything in PHP, and there were many business reasons to do so.
&lt;/p&gt;
&lt;h4&gt;
	#2 — Our entire company’s stuff was in PHP. Don’t underestimate integration.
&lt;/h4&gt;
&lt;p&gt;
	By the old plan (ditching all PHP and doing it all in Rails), there was going to be this One Big Day, where our entire Intranet, Storefront, Members’ Login Area, and dozens of cron shell scripts were ALL going to have to change.
	85 employees re-trained. All customers and clients calling up furious that One Big Day, with questions about the new system.
	Instead, I was able to slowly gut the ugly PHP and replace it with beautiful PHP.
	Launch in stages. No big re-training.
&lt;/p&gt;
&lt;h4&gt;
	#3 — I don’t want what i don’t need
&lt;/h4&gt;
&lt;p&gt;
	I admire the hell out of the Rails core gang that actually understand every line inside Rails itself.
	But I don’t.
	And I’m sure I will never use 90% of it.
	With my little self-made system, every line is only what’s absolutely necessary.
	That makes me extremely happy and comfortable.
&lt;/p&gt;
&lt;h4&gt;
	#4 — It’s small and fast
&lt;/h4&gt;
&lt;p&gt;
	One little 2U server is serving up a ton of cdbaby.com traffic damn fast with hardly any load.
&lt;/p&gt;
&lt;h4&gt;
	#5 — It’s built to my tastes
&lt;/h4&gt;
&lt;p&gt;
	I don’t need to adapt my ways to Rails.
	I tell PHP exactly what I want to do, the way I want to do it, and it doesn’t complain.
	I was having to hack-up Rails with all kinds of plugins and mods to get it to be the multi-lingual integration to our existing 95-table database.
	My new code was made just for me.
	The most efficient possible code to work with our exact needs.
&lt;/p&gt;
&lt;h4&gt;
	#6 — I love SQL
&lt;/h4&gt;
&lt;p&gt;
	Speaking of tastes: one tiny but important thing:
	I love SQL. I dream in queries. I think in tables.
	I was always fighting against Rails and its migrations hiding my beloved SQL from me.
&lt;/p&gt;
&lt;h4&gt;
	#7 — Programming languages are like girlfriends.
	The new one is better because *you* are better
&lt;/h4&gt;
&lt;p&gt;
	Rails was an amazing teacher.
	I loved it’s “do exactly as I say” paint-by-numbers framework that taught me some great guidelines.
	I love Ruby for making me really understand OOP.
	God, Ruby is so beautiful.
	I love you, Ruby.
	But the main reason that any programmer learning any new language thinks the new language is so much better than the old one is because he’s a better programmer now!
	You look back at your old ugly PHP code, compared to your new beautiful Ruby code, and think, “God that PHP is ugly!”
	But don’t forget you wrote that PHP years ago and are unfairly discriminating against it now.
	It’s not the language (entirely).
	It’s you. You’re better now. Give yourself some credit.
&lt;/p&gt;&lt;p&gt;
	Ok. All that being said, I’m looking forward to using Rails some day when I start a brand new project from scratch, with Rails in mind from the beginning.
&lt;/p&gt;&lt;p&gt;
	But I hope that this reaches someone somewhere thinking, “God our old code is ugly. If we only threw it all away and did it all over in Rails, it’d be so much easier!”
&lt;/p&gt;</content>
</entry> 
</feed>