<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<title></title>
	<link href="https://bryce.fisher-fleig.org/feed.xml" rel="self" type="application/atom+xml"/>
	<link href="https://bryce.fisher-fleig.org"/>
	<generator uri="https://www.getzola.org/">Zola</generator>
	<updated>2022-10-21T00:00:00+00:00</updated>
	<id>https://bryce.fisher-fleig.org/feed.xml</id>
	<entry xml:lang="en">
		<title>Running Gui Apps from Docker 101</title>
		<published>2022-10-21T00:00:00+00:00</published>
		<updated>2022-10-21T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/gui-apps-from-docker/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/gui-apps-from-docker/</id>
		<content type="html">&lt;p&gt;I&#x27;ve been using neovim-from-scratch as my IDE recently, and its incredibly brittle. This post takes some baby steps toward getting any desktop GUI app running in a docker container to run on my screen in Ubuntu 20.04. Eventually, I want to get this working on MacOS and using nvim, but for today we&#x27;re trying to get any GUI application running on my screen in Ubuntu.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;x11-basics&quot;&gt;X11 Basics&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mermaid-js.github.io&#x2F;mermaid-live-editor&#x2F;edit#pako:eNpl0M1uwyAMB_BXsXwumpJjDru02x6ARZoUenCDu6CGDxEytar67iOlzQ7jZPn_AwNX7L1mbPA7Uhjgc6cc5NV27cRxD0JYP0_8cuLLwVPUQryC7L6qCiTHnywKl3foTPJxEW3pbqtuOxp2CSoQ8GFsWA4UIB9x_YzrHL-byEd_XkUx03woF4tsfWKw1A_GcQnvI_7KupTs9L_dI4Xkw0rlKnGDlqMlo_MXXJe2wjSwZYVNLjXFk0LlbtnNQVPiN708EpsjjRNvkObk5cX12KQ48xPtDOW59qFuv3GzbY4&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;mermaid.ink&#x2F;img&#x2F;pako:eNpl0M1uwyAMB_BXsXwumpJjDru02x6ARZoUenCDu6CGDxEytar67iOlzQ7jZPn_AwNX7L1mbPA7Uhjgc6cc5NV27cRxD0JYP0_8cuLLwVPUQryC7L6qCiTHnywKl3foTPJxEW3pbqtuOxp2CSoQ8GFsWA4UIB9x_YzrHL-byEd_XkUx03woF4tsfWKw1A_GcQnvI_7KupTs9L_dI4Xkw0rlKnGDlqMlo_MXXJe2wjSwZYVNLjXFk0LlbtnNQVPiN708EpsjjRNvkObk5cX12KQ48xPtDOW59qFuv3GzbY4?type=png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;X11 runs as &lt;em&gt;server&lt;&#x2F;em&gt; on the local users workstation&lt;&#x2F;li&gt;
&lt;li&gt;Desktop applications run as clients, possibly on a remote machine&lt;&#x2F;li&gt;
&lt;li&gt;X11 clients and server communicate over a TCP connection&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Given this architecture, running a GUI inside in a docker container &lt;em&gt;should&lt;&#x2F;em&gt; be fine from X11 perspective. It&#x27;s simply a matter of getting the correct networking setup and configuring the client to communicate with the X11 service.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;networking-101&quot;&gt;Networking 101&lt;&#x2F;h2&gt;
&lt;p&gt;The simplest path to something that works is to share the &lt;a href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;network&#x2F;host&#x2F;&quot;&gt;host network&lt;&#x2F;a&gt; with the docker container. This is &lt;em&gt;not&lt;&#x2F;em&gt; the most secure and we can definitely do better. But doing so will require us to learn more about how X11 clients and servers communicate. The risk is that we could allow malicious applications to impersonate us on the internet by using our IP address and network location. Additionally, the dockerized application could access anything on our own machine over the network. Longer term, locking down to minimum number of X11-specific ports should stop these risks.&lt;&#x2F;p&gt;
&lt;p&gt;To tell docker run a container with host networking we do:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;docker run --net host ...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;pointing-our-client-application-at-the-x11-server&quot;&gt;Pointing our client application at the X11 Server&lt;&#x2F;h2&gt;
&lt;p&gt;X11 clients read the &lt;code&gt;DISPLAY&lt;&#x2F;code&gt; environment variable to discover the X Server. &lt;code&gt;DISPLAY&lt;&#x2F;code&gt; generally takes the form: &lt;code&gt;&amp;lt;hostname&amp;gt;:&amp;lt;display number&amp;gt;&lt;&#x2F;code&gt;. The default value is typically &lt;code&gt;:0&lt;&#x2F;code&gt; -- meaning use display 0 on the localhost. However, on my machine I see &lt;code&gt;DISPLAY&lt;&#x2F;code&gt; is set to a higher number. The easiest way to make sure the docker container has the correct &lt;code&gt;DISPLAY&lt;&#x2F;code&gt; value is to pass that variable from the host to the container:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;docker run -e DISPLAY
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;xeyes&quot;&gt;Xeyes&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ll use the xeyes application as a simple test that things are working. A docker image exists for this at &lt;code&gt;fr3nd&#x2F;xeyes&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;testing-it-out&quot;&gt;Testing It Out&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s open a terminal and do this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;docker run --rm -ti --net=host -e DISPLAY fr3nd&#x2F;xeyes
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If all is well, you should see some crazy eyes in a window following your mouse around. Success!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;getting-fancy&quot;&gt;Getting Fancy&lt;&#x2F;h2&gt;
&lt;p&gt;Now to put a cherry on top, we&#x27;d like to create a desktop icon we can use to launch the crazy eyes application without doing arcane docker magic. After much fumbling, I&#x27;ve discovered this magic:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[Desktop Entry]
Version=1.0
Type=Application
Terminal=false
Exec=&#x2F;usr&#x2F;bin&#x2F;docker run --rm --net=host -e DISPLAY fr3nd&#x2F;xeyes
Name=xeyes
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Save the above inside &lt;code&gt;xeyes.desktop&lt;&#x2F;code&gt; on your desktop. You&#x27;ll also need to right-click on this icon and choose &amp;quot;Allow Launching&amp;quot; before this will work. Double click the icon and you should see the same crazy eyes appear!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;caveats&quot;&gt;Caveats&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Tested on Ubuntu 20.04 LTS -- results may vary on other versions of Ubuntu&lt;&#x2F;li&gt;
&lt;li&gt;docker run must &lt;em&gt;not&lt;&#x2F;em&gt; contain &lt;code&gt;-it&lt;&#x2F;code&gt; or else you must use a terminal which is very annoying&lt;&#x2F;li&gt;
&lt;li&gt;The full path to docker without any &lt;code&gt;~&lt;&#x2F;code&gt; must be set in &lt;code&gt;Exec&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;DISPLAY&lt;&#x2F;code&gt; will only work like this because we&#x27;re running Linux inside and outside the container on the same machine with shared networking. Don&#x27;t expect this value of &lt;code&gt;DISPLAY&lt;&#x2F;code&gt; to work on MacOS&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;helpful-source-material&quot;&gt;Helpful Source Material&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Nassos Michas &lt;a href=&quot;https:&#x2F;&#x2F;betterprogramming.pub&#x2F;running-desktop-apps-in-docker-43a70a5265c4&quot;&gt;post&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;This stackoverflow &lt;a href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;23103683&#x2F;ubuntu-desktop-script-to-open-terminal-navigate-to-a-folder-and-run-compass-wat#23105350&quot;&gt;post&lt;&#x2F;a&gt; concerning permissions&lt;&#x2F;li&gt;
&lt;li&gt;This very specific stackoverflow &lt;a href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;49614736&#x2F;run-a-docker-container-from-a-desktop-file-without-terminal-gui&quot;&gt;post&lt;&#x2F;a&gt; concerning eliminating terminals from showing up&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>How I Nvim-from-Scratch</title>
		<published>2022-10-12T00:00:00+00:00</published>
		<updated>2022-10-12T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/nvim-from-scratch/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/nvim-from-scratch/</id>
		<content type="html">&lt;p&gt;In this post, I&#x27;m going to list out key actions I try to take as headings on the page, and then write the keystrokes necessary to perform each one. It&#x27;s designed to be a cheatsheet for navigating around files and projects quickly using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;LunarVim&#x2F;Neovim-from-scratch&quot;&gt;nvim-from-scratch&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;nvim-tree-actions&quot;&gt;nvim-tree Actions&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve been a loyal NerdTree plugin user for almost a decade, but nvim-from-scratch leverages a new file explorer plugin called &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nvim-tree&#x2F;nvim-tree.lua&#x2F;&quot;&gt;nvim-tree&lt;&#x2F;a&gt;. I refer to this plugin as the &amp;quot;file explorer&amp;quot; below. All of these tips are things you can do using the nvim-tree plugin.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;highlight-current-buffer-s-file-in-file-explorer&quot;&gt;Highlight Current Buffer&#x27;s File in File Explorer&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;leader&amp;gt;e&lt;&#x2F;code&gt; in normal mode from a file buffer&lt;&#x2F;p&gt;
&lt;h3 id=&quot;copy-and-rename-a-file-from-file-explorer&quot;&gt;Copy and Rename a File from File Explorer&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;cpr&lt;&#x2F;code&gt; while hovering over file in file explorer&lt;&#x2F;p&gt;
&lt;h3 id=&quot;open-file-in-vertical-split-from-file-explorer&quot;&gt;Open File in Vertical Split from File Explorer&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;C-v&amp;gt;&lt;&#x2F;code&gt; while hovering over file in file explorer&lt;&#x2F;p&gt;
&lt;h3 id=&quot;open-file-in-horizontal-split-from-file-explorer&quot;&gt;Open File in Horizontal Split from File Explorer&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;C-x&amp;gt;&lt;&#x2F;code&gt; while hovering on file in file explorer&lt;&#x2F;p&gt;
&lt;h3 id=&quot;open-file-in-tab-from-file-explorer&quot;&gt;Open File in Tab from File Explorer&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;C-t&amp;gt;&lt;&#x2F;code&gt; while hovering on file in file explorer&lt;&#x2F;p&gt;
&lt;h3 id=&quot;previous-next-uncommitted-change-in-file-explorer&quot;&gt;Previous&#x2F;Next Uncommitted Change in File Explorer&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Next: &lt;code&gt;]c&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Previous: &lt;code&gt;[c&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;toggle-hidden-files&quot;&gt;Toggle Hidden Files&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;dot files: &lt;code&gt;H&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;git ignored files: &lt;code&gt;I&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;move-up-a-directory-in-file-explorer&quot;&gt;Move Up a Directory in File Explorer&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;-&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;close-file-explorer&quot;&gt;Close File Explorer&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;q&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;more-default-keybindings&quot;&gt;More Default keybindings&lt;&#x2F;h3&gt;
&lt;p&gt;See &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nvim-tree&#x2F;nvim-tree.lua&#x2F;blob&#x2F;b01e7beaa6f0dbbf5df775cf4ecc829a23f0be54&#x2F;doc&#x2F;nvim-tree-lua.txt#L1240-L1241&quot;&gt;nvim-tree&lt;&#x2F;a&gt; repo&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lsp-actions&quot;&gt;LSP Actions&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;goto-definition&quot;&gt;goto definition&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;gd&lt;&#x2F;code&gt; from normal mode while hovering on symbol&lt;&#x2F;p&gt;
&lt;h3 id=&quot;goto-references&quot;&gt;goto references&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;gr&lt;&#x2F;code&gt; from normal mode while hovering on symbol&lt;&#x2F;p&gt;
&lt;h2 id=&quot;telescope-actions&quot;&gt;Telescope Actions&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;fuzzy-search-files-by-name&quot;&gt;Fuzzy Search Files by Name&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;leader&amp;gt;f&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ripgrep&quot;&gt;Ripgrep&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;populate-quickfix-window-with-ripgrep-search-results&quot;&gt;Populate quickfix window with ripgrep search results&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;\&lt;&#x2F;code&gt; from normal mode&lt;&#x2F;p&gt;
&lt;h2 id=&quot;vim-rhubarb&quot;&gt;vim-rhubarb&lt;&#x2F;h2&gt;
&lt;p&gt;Didn&#x27;t know this existed but now can&#x27;t live without it:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;open-current-buffer-on-github-com-in-browser&quot;&gt;Open current buffer on Github.com in browser&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;:GBrowse&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;living-doc&quot;&gt;Living Doc&lt;&#x2F;h2&gt;
&lt;p&gt;My intention is to update this doc as I learn more shortcuts worth remembering.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Quick Notes on SQLite Capabilities</title>
		<published>2021-12-04T00:00:00+00:00</published>
		<updated>2021-12-04T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/quick-notes-on-sqlite-capabilities/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/quick-notes-on-sqlite-capabilities/</id>
		<content type="html">&lt;p&gt;I&#x27;ve been reading up on and learning a lot about SQLite recently for some personal projects. I wanted to preserve a lot of the awesome tools and techniques I&#x27;ve learned over the past month or two before I forget.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;biggest-advantage-app-and-db-in-one-process&quot;&gt;Biggest Advantage - App and DB in One Process&lt;&#x2F;h2&gt;
&lt;p&gt;Build apps with no separate database server infrastructure required. As an added bonus, use the &lt;code&gt;sqlite3&lt;&#x2F;code&gt; cli to query the db directly even when not running the app.&lt;&#x2F;p&gt;
&lt;p&gt;This has lead to the common pattern of every Python app in existence using SQLite for the quickstart guide. Here&#x27;s three other projects I&#x27;m aware of that go beyond a quick start to be powered exclusively by SQLite:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;datasette.io&#x2F;&quot;&gt;Datasette&lt;&#x2F;a&gt; - like Superset&#x2F;Tableau for SQLite on steriods&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;sql.js.org&#x2F;#&#x2F;&quot;&gt;SQL.js&lt;&#x2F;a&gt; - SQLite compiled to Javascript enabling use cases like &lt;a href=&quot;https:&#x2F;&#x2F;phiresky.github.io&#x2F;blog&#x2F;2021&#x2F;hosting-sqlite-databases-on-github-pages&#x2F;&quot;&gt;hosting sqlite databases on Github pages&lt;&#x2F;a&gt; -- no really, that blog post is way more impressive than anything I&#x27;ve ever even attempted in my whole career.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;crawshaw.io&#x2F;blog&#x2F;one-process-programming-notes&quot;&gt;One Process Programming Notes&lt;&#x2F;a&gt; - a whole philosophy embracing solo developer constraints, particularly rejecting horizontal scalability&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;drawback-no-elasticity&quot;&gt;Drawback - No Elasticity&lt;&#x2F;h3&gt;
&lt;p&gt;Because the database is really just a file, not a separate server that could be relocated to another host, you can only run one container&#x2F;host&#x2F;pod of your application server. Your application server &lt;em&gt;must&lt;&#x2F;em&gt; have enough CPU, memory and disk available to run the application, store all your data, execute queries, and other work the OS does in the background. It&#x27;s impossible to separate out the database onto a different &amp;quot;physical&amp;quot; server or run multiple application servers. This means SQLite is generally not a good fit for many services you&#x27;d want to be able to scale in and out at your day job.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;workarounds-for-lack-of-elasticity&quot;&gt;Workarounds for Lack of Elasticity&lt;&#x2F;h3&gt;
&lt;p&gt;There&#x27;s a few ways you can get around this limitation, but nothing will replace a separate database server for truly elastical applications. Here&#x27;s a few tricks and tools:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;dqlite.io&#x2F;&quot;&gt;DQLite&lt;&#x2F;a&gt; - Distributed SQLite. I&#x27;m not really up to speed on this, but it seems to embed a special SQLite database that uses a Raft implementation to manage a cluster of SQLite databases, one of which is a leader. Kind of an interesting middle ground -- your application becomes stateful-ish&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;engineering.backtrace.io&#x2F;2021-12-02-verneuil-s3-backed-asynchronous-replication-for-sqlite&#x2F;&quot;&gt;verneuil&lt;&#x2F;a&gt; - if stale reads are acceptable, this project makes it possible to synchronously replicate to S3 and read back from S3 if local files are lost or unusable for some reason. The engineers combine this technique with sharding, described below.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Shard_(database_architecture)&quot;&gt;Sharding&lt;&#x2F;a&gt; - this is an old trick, but if your dataset can be easily and cleanly broken out by customer id, geographic region, or some other primary key in your data model, you could just run one separate SQLite per &amp;quot;shard&amp;quot;. The issue is that you could &lt;em&gt;never&lt;&#x2F;em&gt; ever query across the shard without basically reimplementing DQLite. You can combine sharding with &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Consistent_hashing&quot;&gt;consistent hashing&lt;&#x2F;a&gt; to minimize movement of records between shards when a new shard is added or removed.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Network_File_System&quot;&gt;NFS&lt;&#x2F;a&gt; - theoretically, maybe, you could use a &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;storage&#x2F;persistent-volumes&#x2F;&quot;&gt;kubernetes persistent volume&lt;&#x2F;a&gt; shared by all pods running your application to host the SQLite database. Or perhaps some other kind of network mounted file system (NFS), or just a &lt;a href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;engine&#x2F;extend&#x2F;plugins_volume&#x2F;&quot;&gt;docker volume plugin&lt;&#x2F;a&gt; so that the file system itself is replicated instead of your database. I think this actually has a low probability of success because I don&#x27;t think SQLite was made for this and likely you would undermine all the lovely guarrantees SQLite makes. Plus, even if it somehow worked, you would likely degrade your database performance pretty significantly.&lt;&#x2F;li&gt;
&lt;li&gt;Leader &#x2F; Worker Pattern - okay, so imagine you have some really high CPU workload like rendering computer graphics, and all you need to do is provide an API to submit jobs and track the work. Rather than make all servers work the same way, make one &amp;quot;kind&amp;quot; of server (the &amp;quot;Leader&amp;quot; or &amp;quot;Coordinator&amp;quot;) run a SQLite database to track the jobs state and where they are assigned, etc. Then make stateless &amp;quot;Workers&amp;quot; that actually do the CPU intensive work, and talk to the leader to store and query all state. While this system can provide serious elasticity, there&#x27;s not that much state to track, and a single SQLite database and application might be sufficient to serve a decent scaled system.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Probably though, if you start outgrowing a single server to run your application, you may have outgrown SQLite.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;drawback-potential-data-loss&quot;&gt;Drawback - Potential Data Loss&lt;&#x2F;h3&gt;
&lt;p&gt;Because you have to store your stateful database on the same &amp;quot;physical&amp;quot; server (really this could be bare metal, VM, or a container) as your application, if anything happens to your &amp;quot;physical server&amp;quot; then you also lose all the data too.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;workarounds-for-potential-data-loss&quot;&gt;Workarounds for Potential Data Loss&lt;&#x2F;h3&gt;
&lt;p&gt;The main ways to prevent data loss are backup or replication. Whereas backup is generally something that is done hourly or daily via an external cronjob, replication is something that generally happens continuously using internals of the database (such as the Write-Ahead-Log). Here&#x27;s two replication systems:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;litestream.io&#x2F;&quot;&gt;LiteStream&lt;&#x2F;a&gt; - side car process that asynchronously replicates to S3, can be separate binary or container&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;engineering.backtrace.io&#x2F;2021-12-02-verneuil-s3-backed-asynchronous-replication-for-sqlite&#x2F;&quot;&gt;verneuil&lt;&#x2F;a&gt; - a virtual file system (&amp;quot;vfs&amp;quot;) loadable extension for SQLite that synchronously replicates to S3.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To my mind, LiteStream should in theory be the safer option because no matter what kind of downtime or connectivity issues you have reaching S3, the data on your local machine could always be backed up at a later point when connectivity is restored. Whereas, with verneuil I&#x27;d worry that if my connectivity to S3 was problematic, writes to my local disk might also fail causing data loss. I haven&#x27;t examined either implementation to evaluate the quality or failure modes&#x2F;guarrantees.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, SQLite itself ships with a few flavors of backup APIs. However, these backup APIs require your application to trigger the backup explicitly, rather than automatically and continuously replicating. Generally, I&#x27;d prefer to use replication because it means less code for me write and it minimizes data loss by staying very close to live writes. Having said that, backups are really amazing when you discover that the recent data is all wrong you and you need to go back 7 days to when you shipped a horrible bug. Anywho, here are some backup options:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;backup.html&quot;&gt;Online Backup API&lt;&#x2F;a&gt; - Really excellent docs here. Main advantage is minimal locking so that you can keep on using the database without getting frozen during backup&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;lang_vacuum.html#vacuuminto&quot;&gt;VACUUM INTO&lt;&#x2F;a&gt; - &lt;code&gt;VACUUM INTO&lt;&#x2F;code&gt; &lt;em&gt;does&lt;&#x2F;em&gt; lock up the database significantly. However, it clears out all any leftover traces of old records and transactions so it guarrantees the smallest backup file size. Neat!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;bonus-feature-full-text-search&quot;&gt;Bonus Feature - Full Text Search&lt;&#x2F;h2&gt;
&lt;p&gt;Want to provide a search functionality in your app, but don&#x27;t want to run a specialized database? SQLite has you covered: &lt;a href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;fts5.html&quot;&gt;Full Text Search Docs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bonus-feature-common-table-expressions-ctes&quot;&gt;Bonus Feature - Common Table Expressions (CTEs)&lt;&#x2F;h2&gt;
&lt;p&gt;So, I always vaguely am aware that CTEs exist to make SQL queries simpler, but I&#x27;ve never fully grokked it. Here&#x27;s a great &lt;a href=&quot;https:&#x2F;&#x2F;blog.expensify.com&#x2F;2015&#x2F;09&#x2F;25&#x2F;the-simplest-sqlite-common-table-expression-tutorial&#x2F;&quot;&gt;intro to CTEs for sqlite&lt;&#x2F;a&gt; blog post. But, really, the only data I really ever want to analyze is time series data, and it turns out &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@vsbabu&#x2F;sqlite3-cte-tricks-for-time-series-analysis-196dbf3ffdf9&quot;&gt;CTEs that really help with times series&lt;&#x2F;a&gt; too. The basics of the syntax are &lt;a href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;lang_with.html&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Since, I really love doing time series work, it turns out there&#x27;s actually a special index data structure available called &lt;a href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;rtree.html&quot;&gt;R*Trees&lt;&#x2F;a&gt; optimized for range queries.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bonus-feature-json-support&quot;&gt;Bonus Feature - JSON Support&lt;&#x2F;h2&gt;
&lt;p&gt;I guess most all the popular databases have this now, but SQLite provides a loadable extension to make it possible to &lt;a href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;json1.html&quot;&gt;modify, query and aggregate fields from JSON valued columns&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;blobs&quot;&gt;Blobs&lt;&#x2F;h2&gt;
&lt;p&gt;So, you can even have columns that have blobs of data in them (think photos or pdfs) and &lt;a href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;c3ref&#x2F;blob_open.html&quot;&gt;incrementally stream data into that column or out&lt;&#x2F;a&gt; of that column. If you really wanted to get crazy, you could replace minio (sorta-kinda) with SQLite blobs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;extensibility&quot;&gt;Extensibility&lt;&#x2F;h2&gt;
&lt;p&gt;Kind of insane, but &lt;a href=&quot;https:&#x2F;&#x2F;sqlite.org&#x2F;appfunc.html&quot;&gt;you can write and dynamically load extensions&lt;&#x2F;a&gt; into a running SQLite library if those extensions are written in (something compiles to) C&#x27;s ABI. These extensions can add scalar and aggregations functions to the pre-existing SQL syntax.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;random-tools&quot;&gt;Random Tools&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;sqlitebrowser.org&#x2F;&quot;&gt;DB Browser for SQLite&lt;&#x2F;a&gt; - A desktop GUI for SQLite&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.acquireforensics.com&#x2F;blog&#x2F;analyze-sqlite-database.html&quot;&gt;SQLite Forensics&lt;&#x2F;a&gt; - how to look for deleted entries as part of digital forensics of browsers.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;rusqlite&quot;&gt;Rusqlite&lt;&#x2F;a&gt; - Most popular SQLite library for Rust&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;What tools are you using to help you live with SQLite?&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Backup Options for Open Source SQL Databases</title>
		<published>2021-08-10T00:00:00+00:00</published>
		<updated>2021-08-10T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/backup-options-for-open-source-sql-databases/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/backup-options-for-open-source-sql-databases/</id>
		<content type="html">&lt;p&gt;I&#x27;ve been trying to setup a kind of mini datawarehouse for my homelab, and I&#x27;m spoiled for choice. As I&#x27;m wont to do on this blog, I&#x27;m going to do a technical survey of the various options that seem interesting to me.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;criteria&quot;&gt;Criteria&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Convenient Setup&lt;&#x2F;strong&gt; - installing and configuring and maintaining that config over time is just too hard. Let&#x27;s use an off-the-shelf (base) docker image from a reputable source. We don&#x27;t want to have lots of docker containers for this either. Ideally, just one container, not like 5 docker-compose services.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;General Purpose&lt;&#x2F;strong&gt; - we might be doing time series analysis, full text search, point queries, or crazy joining. I don&#x27;t want to lock in right now nor use many databases. I want one single database to setup that can handle whatever kind of read load I throw at it at very low scale with okay performance&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Massive Ecosystem&lt;&#x2F;strong&gt; - I&#x27;d like to plugin this database into Superset, various custom glue code applications, and make the value of the data I collect as great as possible. Big ecosystems generally have lots of stackoverflow, good official docs, etc.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Convenient high-frequency backup and restore&lt;&#x2F;strong&gt; - For a upcycled homelab situation, servers get unplugged, spontaneously restart, or just fail all the time. I want to leverage cloud storage for backup&#x2F;restore to minimize data loss at rest. When I plug the server back in, I want to make sure I can easily recover as much data from before the power incident as possible.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So, while I keep telling myself I love NoSQL databases (and I &lt;em&gt;think&lt;&#x2F;em&gt; that&#x27;s still true?), I&#x27;m really looking at the most well known databases here and their upstart&#x2F;mostly compatible twins. Sorry Mongo and DynamoDB! As much I&#x27;d love to use Presto&#x2F;Hive or even Clickhouse to be able to query directly against something like S3 or minio, all of those database options have &lt;em&gt;far&lt;&#x2F;em&gt; too many moving parts and would require 2-5 docker containers just for a hello world style beginning. Once we&#x27;ve windled down the list this far, then probably the ease &amp;amp; power of the backups really starts to look like the differentiating factor.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-contenders&quot;&gt;The Contenders&lt;&#x2F;h2&gt;
&lt;p&gt;Based on the above criteria, I&#x27;ll be looking at:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Postgres&lt;&#x2F;li&gt;
&lt;li&gt;MySQL&lt;&#x2F;li&gt;
&lt;li&gt;MariaDB&lt;&#x2F;li&gt;
&lt;li&gt;CockroachDB&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;flavors-of-backup&quot;&gt;Flavors of Backup&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s really hard to compare the various backup options offered by different databases without having a detailed understanding of the various mechanisms for backup that exist and the inherent tradeoffs in each. Before diving into the databases, I&#x27;m going to try to compare and contrast these different styles of backup.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Flavor&lt;&#x2F;th&gt;&lt;th&gt;Trigger&lt;&#x2F;th&gt;&lt;th&gt;Whole dataset?&lt;&#x2F;th&gt;&lt;th&gt;Locking&lt;&#x2F;th&gt;&lt;th&gt;FS Tweaks Needed&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Logical Backup&lt;&#x2F;td&gt;&lt;td&gt;Normally cron&lt;&#x2F;td&gt;&lt;td&gt;Optionally&lt;&#x2F;td&gt;&lt;td&gt;Table&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;File System Snapshot&lt;&#x2F;td&gt;&lt;td&gt;Sys admin&lt;&#x2F;td&gt;&lt;td&gt;Mandatory&lt;&#x2F;td&gt;&lt;td&gt;Server&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Replication&lt;&#x2F;td&gt;&lt;td&gt;DB Activity&lt;&#x2F;td&gt;&lt;td&gt;Impractical&lt;&#x2F;td&gt;&lt;td&gt;None&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;replication-sensation&quot;&gt;Replication Sensation&lt;&#x2F;h3&gt;
&lt;p&gt;What I&#x27;m calling &amp;quot;replication&amp;quot; here has many different flavors itself. For all flavors of replication, for all practical purposes, you must have one of the other forms of backup to anchor the changes since the last backup. Restoring then requires a sequence of commands of find the appropriate full backup, and then replay the replication data &amp;quot;on top of&amp;quot; the full backup.&lt;&#x2F;p&gt;
&lt;p&gt;Postgres leverages it&#x27;s own Write-Ahead-Logs (&amp;quot;WAL&amp;quot;) to enable consistent crash recovery, and it exposes the ability for server admins to automatically &amp;quot;archive&amp;quot; these WALs with arbitrary scripts. Those scripts could upload WALs to S3, etc. However, if uploading the WALs ever fails, it can cause postgres to become unavailable. If you give up on a WAL, then your backup will have lost data.&lt;&#x2F;p&gt;
&lt;p&gt;Replicating the logs to another server and forming a cluster is also a form of backup, in that it offers a &amp;quot;backup&amp;quot; server in case anything happens to the master. It can also be used to reduce extra load on a leader during a full backup, or even theoretically used to restore a new master. Since replication doesn&#x27;t really allow us to ease do backup to a commodity cloud storage system, I&#x27;m going to rule out this kind of replication.&lt;&#x2F;p&gt;
&lt;p&gt;Mysql accomplishes it&#x27;s replication to other servers using the binary log or &amp;quot;binlog&amp;quot; that contains all mutation-inducing queries. Like Postgres leveraging WALs for incremental backups, MySQL can be configured to leverage the binlog for incremental backups to a file. Unlike the WALs, MySQL is less vulnerable to network blips taking down the database server. However configuring and testing and monitoring the binlog.&lt;&#x2F;p&gt;
&lt;p&gt;While a PiT restore capability would be great to setup, this is an advanced features that won&#x27;t work without some kind of full backup solution already in place. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;logical-backups&quot;&gt;&amp;quot;Logical&amp;quot; Backups&lt;&#x2F;h3&gt;
&lt;p&gt;Logical backups can dump everything in a database to a text file filled with SQL statements. At a later time or on a separate machine, those statements can be executed to restore the database to its state at the time of the backup. They are potentially large to store, but easy to restore and widely supported. Logical backups can be run remotely over a TCP or UDP connection in case we want to run an external cronjob or manually trigger this without SSH access. Conveniently, a full logical backup can run on selective tables of a given database.&lt;&#x2F;p&gt;
&lt;p&gt;In the case of MySQL, &lt;code&gt;mysqldump&lt;&#x2F;code&gt; can even output CSV or other formats. Postgres and MariaDB do similar things. Technically, things can change while you run the backup, so its not guarranteed to be perfectly consistently.&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;ll still need to setup a cronjob to run mysqldump and friends, then upload to S3...which means installing an S3 client and configuring that as well. Here&#x27;s a prepackaged &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;itbm&#x2F;postgresql-backup-s3&quot;&gt;scheduled backup to s3&lt;&#x2F;a&gt; for Postgres that looks promising.&lt;&#x2F;p&gt;
&lt;p&gt;CockroachDB actually has full support for backups to S3 on a schedule &lt;a href=&quot;https:&#x2F;&#x2F;www.cockroachlabs.com&#x2F;docs&#x2F;v21.1&#x2F;create-schedule-for-backup&quot;&gt;baked in&lt;&#x2F;a&gt;. The winner here is CockroachDB, since it&#x27;s the simplest. Just run a command one time, and CockroachDB will make the backup on a schedule and upload it for you.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;snapshot-backups&quot;&gt;Snapshot Backups&lt;&#x2F;h3&gt;
&lt;p&gt;Postgres, MySQL and MariaDB all provide facilities for &amp;quot;snapshot&amp;quot; backup of the underlying file system with various caveats. The main problems are (1) a database server in motion might be snapshot at a bad moment that is inconsistent and (2) a database server might some key information in RAM, but not yet on disk.&lt;&#x2F;p&gt;
&lt;p&gt;To mitigate these issues, the client must lock the database and then &lt;code&gt;tar&lt;&#x2F;code&gt; up the various files of interest while holding that lock in an ongoing connection. Finally, the client must release the connection. It might be possible to script that with bash, but it would annoying. I&#x27;m not sure why a tool that does this for you is not simply included in these database products.&lt;&#x2F;p&gt;
&lt;p&gt;Using similar techniques, file system level backups can also be taken using ZFS or BTRFS (or LVM too). However, those snapshots can&#x27;t be exported to cloud storage.&lt;&#x2F;p&gt;
&lt;p&gt;I looked at &lt;code&gt;docker checkpoint&lt;&#x2F;code&gt; as well, but this won&#x27;t work because it only captures the running processes memory, not disk. Sadly &lt;code&gt;docker commit&lt;&#x2F;code&gt; would probably also require downtime to backup the database, though in this case would be acceptable. However, the new docker image would still need to exported for cloud backup and would likely be even larger than a logical backup.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Based purely on the backup criteria, my small dataset, and some tolerance for data loss over complexity, I think I&#x27;m going to use the open source &amp;quot;core&amp;quot; CockroachDB&#x27;s full backup schedule and just do full backups hourly with a 1-2 week retention.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Fun with Histograms - Prometheus Basics</title>
		<published>2021-07-27T00:00:00+00:00</published>
		<updated>2021-07-27T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/prometheus-histograms/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/prometheus-histograms/</id>
		<content type="html">&lt;p&gt;Building on my previous learning exercises, I need to learn how to query &lt;a href=&quot;https:&#x2F;&#x2F;prometheus.io&#x2F;&quot;&gt;Prometheus&lt;&#x2F;a&gt; so that I can work with production systems running on Kubernetes. For large scale datasets, I find histograms to be an excellent tool for summarizing and visualizing throughput and latency. However, this is all a bit confusing and new to me in Prometheus.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summaries-histograms-oh-my&quot;&gt;Summaries, Histograms, Oh My!&lt;&#x2F;h2&gt;
&lt;p&gt;Prometheus has two metric types for histograms -- Summaries and Histograms.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;summaries&quot;&gt;Summaries&lt;&#x2F;h3&gt;
&lt;p&gt;Summaries are calculated client side, meaning the CPU must dedicated cycles to computing these values it could be serving your customers. Exact percentiles are precomputed and stored in a ready-to-use state in Prometheus. However, you can&#x27;t calculate any new percentiles that weren&#x27;t explicitly calculated ahead of time. You can think of a Summary as being stored inside Prometheus something like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2021&#x2F;prometheus-representing-summaries.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can see in the diagram that the metric name provided in the application code &lt;code&gt;http_response_duration_ms&lt;&#x2F;code&gt; is stored with explicit labels for the exact percentile (called &lt;code&gt;quartiles&lt;&#x2F;code&gt;). You can retrieve any of those time series with an instant selector, but you can&#x27;t apply any new functions. Here&#x27;s a sample PromQL for the median value from the diagram above:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-prom&quot; data-lang=&quot;prom&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;http_response_duration_ms{quartile=&amp;quot;0.5&amp;quot;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Sadly, because the percentiles are all precomputed, you can&#x27;t combine this value from multiple kubernetes pods at query time. Some other system would have to calculate a Summary ahead of time...so we&#x27;re not going to talk about Summaries anymore.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;histograms&quot;&gt;Histograms&lt;&#x2F;h3&gt;
&lt;p&gt;Histograms are calculated server side, within Prometheus itself at query time. Prometheus stores histograms internally in buckets that have a max size (labeled &lt;code&gt;le&lt;&#x2F;code&gt;), but no minimum size. You must configure the number and max size of each bucket ahead of time. Each bucket time series will contain the count of observations that was less than or equal to its &lt;code&gt;le&lt;&#x2F;code&gt; value for a given timestamp.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2021&#x2F;prometheus-representing-histograms.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Things look quite a bit different. Each bucket is stored in a separate metric suffixed with &lt;code&gt;_bucket&lt;&#x2F;code&gt; and the maximum value for that time series is in the label &lt;code&gt;le&lt;&#x2F;code&gt;. There is always a largest bucket with infinite maximum value &lt;code&gt;{le=&amp;quot;Inf&amp;quot;}&lt;&#x2F;code&gt; which will always have the same value as &lt;code&gt;_count&lt;&#x2F;code&gt;. Because you can&#x27;t meaningfully use the buckets directly AFAICT, you generally use the function &lt;a href=&quot;https:&#x2F;&#x2F;prometheus.io&#x2F;docs&#x2F;prometheus&#x2F;latest&#x2F;querying&#x2F;functions&#x2F;#histogram_quantile&quot;&gt;histogram_quantile()&lt;&#x2F;a&gt; to estimate whatever quantile you want. The accuracy will be based mostly on the sizes of buckets you choose in the client. The more and smaller the buckets, the more accurate but the more data required to store and compute values.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;querying-histograms&quot;&gt;Querying Histograms&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s start playing with the &lt;code&gt;histogram_quantile()&lt;&#x2F;code&gt; function. Here&#x27;s a simple PromQL query against the histogram for the diagram:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-prom&quot; data-lang=&quot;prom&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;histogram_quantile(.99, rate(http_response_duration_ms[1m]))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Even though this is the simplest usage of &lt;code&gt;histogram_quantile()&lt;&#x2F;code&gt;, there&#x27;s kind of a lot. Let&#x27;s break it down:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.99&lt;&#x2F;code&gt; - this is the &amp;quot;rank&amp;quot; or the &amp;quot;percentile&amp;quot; being called in a range from 0 to 1&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;rate()&lt;&#x2F;code&gt; - we&#x27;ll talk more about this, but basically this converts a counter metric into a usable form&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;[1m]&lt;&#x2F;code&gt; - we&#x27;re aggregating at 1m intervals here&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;counters-and-rate&quot;&gt;Counters and &lt;code&gt;rate()&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;If you read the docs, you&#x27;ll see that counters are &amp;quot;monotonically&amp;quot; increasing. That means that a given metric name with a given set of labels will always increase, but the problem is graphing a line that always goes up just isn&#x27;t very helpful. So, &lt;code&gt;rate&lt;&#x2F;code&gt; will break down time slices, see the increase during the time slice, and make that the value. Let&#x27;s take some observations at different times of a counter as an example:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Time&lt;&#x2F;th&gt;&lt;th&gt;Counter Value&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;T1&lt;&#x2F;td&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;T2&lt;&#x2F;td&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;T3&lt;&#x2F;td&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;T4&lt;&#x2F;td&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;T5&lt;&#x2F;td&gt;&lt;td&gt;17&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;quickchart.io&#x2F;chart?bkg=white&amp;amp;c=%7B%0A%20%20type%3A%20%27line%27%2C%0A%20%20data%3A%20%7B%0A%20%20%20%20labels%3A%20%5B%27T1%27%2C%20%27T2%27%2C%20%27T3%27%2C%20%27T4%27%2C%20%27T5%27%5D%2C%0A%20%20%20%20datasets%3A%20%5B%7B%0A%20%20%20%20%20%20label%3A%20%27Counter%20Value%27%2C%0A%20%20%20%20%20%20data%3A%20%5B3%2C%205%2C%207%2C%207%2C%2017%5D%0A%20%20%20%20%7D%5D%0A%20%20%7D%0A%7D&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So, if we apply &lt;code&gt;rate()&lt;&#x2F;code&gt; to this time series, then the values will be changed like this:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Time&lt;&#x2F;th&gt;&lt;th&gt;&lt;code&gt;rate(counter)&lt;&#x2F;code&gt; Value&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;T1&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;T2&lt;&#x2F;td&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;T3&lt;&#x2F;td&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;T4&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;T5&lt;&#x2F;td&gt;&lt;td&gt;10&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;quickchart.io&#x2F;chart?bkg=white&amp;amp;c=%7B%0A%20%20type%3A%20%27line%27%2C%0A%20%20data%3A%20%7B%0A%20%20%20%20labels%3A%20%5B%27T1%27%2C%20%27T2%27%2C%20%27T3%27%2C%20%27T4%27%2C%20%27T5%27%5D%2C%0A%20%20%20%20datasets%3A%20%5B%7B%0A%20%20%20%20%20%20label%3A%20%27Counter%20Value%27%2C%0A%20%20%20%20%20%20data%3A%20%5B0%2C%202%2C%202%2C%200%2C%2010%5D%0A%20%20%20%20%7D%5D%0A%20%20%7D%0A%7D&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So, basically, because sending histograms to Prometheus generates a whole bunch of different counters (specifically the &lt;code&gt;_bucket&lt;&#x2F;code&gt; metric names) we normally need to use &lt;code&gt;rate()&lt;&#x2F;code&gt; first on the get usable data.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;aggregating-histograms-by-series&quot;&gt;Aggregating Histograms by Series&lt;&#x2F;h3&gt;
&lt;p&gt;So, let&#x27;s say you have a lot of pods running a service and you add labels to all your metrics that includes the pod. For this particular metric, you&#x27;ve also included the http status code. However, you now want to build a dashboard out of one metric showing the 95th percentile of something, for example &lt;code&gt;http_response_duration_ms&lt;&#x2F;code&gt;. If you naively use the code I shared above you&#x27;ll see a time series graphed for the combination of each unique pod and status code!!&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s not do that. Instead try this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-prom&quot; data-lang=&quot;prom&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;histogram_quantile(.99, sum(rate(http_response_duration_ms[1m])) by (le, status_code))
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The main thing that&#x27;s new and different here is that &lt;code&gt;sum(..) by (..)&lt;&#x2F;code&gt; will look for &lt;em&gt;any&lt;&#x2F;em&gt; series that share the same labels &lt;code&gt;{le=.., status_code=..}&lt;&#x2F;code&gt; and add all their values together into a single series. Let&#x27;s take an example using a single sample&#x2F;timestamp across many series to show what happens:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;le&lt;&#x2F;th&gt;&lt;th&gt;pod&lt;&#x2F;th&gt;&lt;th&gt;status_code&lt;&#x2F;th&gt;&lt;th&gt;value&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;500&lt;&#x2F;td&gt;&lt;td&gt;pod1&lt;&#x2F;td&gt;&lt;td&gt;200&lt;&#x2F;td&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;500&lt;&#x2F;td&gt;&lt;td&gt;pod2&lt;&#x2F;td&gt;&lt;td&gt;200&lt;&#x2F;td&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1000&lt;&#x2F;td&gt;&lt;td&gt;pod3&lt;&#x2F;td&gt;&lt;td&gt;200&lt;&#x2F;td&gt;&lt;td&gt;10&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1000&lt;&#x2F;td&gt;&lt;td&gt;pod4&lt;&#x2F;td&gt;&lt;td&gt;200&lt;&#x2F;td&gt;&lt;td&gt;10&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1000&lt;&#x2F;td&gt;&lt;td&gt;pod5&lt;&#x2F;td&gt;&lt;td&gt;404&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;After running the promql above, the above series sum up any series that have the same &lt;code&gt;le&lt;&#x2F;code&gt; and &lt;code&gt;status_code&lt;&#x2F;code&gt;, so the end result looks like:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;le&lt;&#x2F;th&gt;&lt;th&gt;status_code&lt;&#x2F;th&gt;&lt;th&gt;value&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;500&lt;&#x2F;td&gt;&lt;td&gt;200&lt;&#x2F;td&gt;&lt;td&gt;10 &lt;em&gt;(5+5)&lt;&#x2F;em&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1000&lt;&#x2F;td&gt;&lt;td&gt;200&lt;&#x2F;td&gt;&lt;td&gt;20 &lt;em&gt;(10+10)&lt;&#x2F;em&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1000&lt;&#x2F;td&gt;&lt;td&gt;404&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Now, you&#x27;ll see one series per status code across your whole cluster. If we had any other labels (like &lt;code&gt;endpoint&lt;&#x2F;code&gt;, &lt;code&gt;service&lt;&#x2F;code&gt;, etc), those would also disappear after the aggregation above.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;don-t-forget-your-le&quot;&gt;Don&#x27;t Forget Your &lt;code&gt;le&lt;&#x2F;code&gt;!&lt;&#x2F;h3&gt;
&lt;p&gt;Behind the scenes, at query time, the &lt;code&gt;histogram_quantile()&lt;&#x2F;code&gt; function will &lt;em&gt;secretly&lt;&#x2F;em&gt; look for all labels of &lt;code&gt;{le=..}&lt;&#x2F;code&gt; on the provided series. Before I understood this, I kept hitting errors saying:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;No datapoints found.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Once you know that &lt;code&gt;histogram_quantile()&lt;&#x2F;code&gt; requires the time series passed in to have the labels &lt;code&gt;{le=..}&lt;&#x2F;code&gt;, this actually makes sense. This is why anytime we combine histogram bucket series, we must be careful to preserve the &lt;code&gt;le&lt;&#x2F;code&gt; labels.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Using histograms as a lens, we&#x27;ve dug deep into how Prometheus:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;expects data to come from client applications&lt;&#x2F;li&gt;
&lt;li&gt;stores time series&lt;&#x2F;li&gt;
&lt;li&gt;converts counters into usable time series using &lt;code&gt;rate()&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;exposes histograms via &lt;code&gt;histogram_quantile()&lt;&#x2F;code&gt; function&lt;&#x2F;li&gt;
&lt;li&gt;allows us to combine different labels for a metric name&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Hopefully, this lets you start doing interesting analysis of your production systems!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Java LSP for Neovim - Java Basics</title>
		<published>2021-07-15T00:00:00+00:00</published>
		<updated>2021-07-15T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/coc-java/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/coc-java/</id>
		<content type="html">&lt;p&gt;This time in my continuing forray into Java, I&#x27;m going to setup a Language Server Protocol for Neovim.&lt;&#x2F;p&gt;
&lt;p&gt;Quick Notes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;LSP&amp;quot; stands for &lt;a href=&quot;https:&#x2F;&#x2F;microsoft.github.io&#x2F;language-server-protocol&#x2F;&quot;&gt;Language Server Protocol&lt;&#x2F;a&gt;, and its an awesome standard invented by Microsoft for VSCode designed to help everyone stop reinventing the wheel. The gist is that anyone figures out how to do things like goto-definition for a programming language, all editors that speak the protocol can immediately start using that one implementation. I use this acronym excessively in this article, so I figure it&#x27;s worth explaining here first :)&lt;&#x2F;li&gt;
&lt;li&gt;Probably not important, but just FYI I&#x27;m doing all this on Linux. Generally things will actually be easier for you on Mac or Windows (sorry BSDs) than for me.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;tl-dr-openjdk11-coc-nvim-coc-java&quot;&gt;TL;DR - OpenJDK11 + coc.nvim + coc-java&lt;&#x2F;h2&gt;
&lt;p&gt;After trying some other things, I eventually found that &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;neoclide&#x2F;coc.nvim&quot;&gt;Conquer-of-Complete (aka &amp;quot;CoC&amp;quot;)&lt;&#x2F;a&gt; really does JustWork (tm). Although, it doesn&#x27;t advertise the Java LSP implementation, it&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;opensourcelibs.com&#x2F;lib&#x2F;coc-java&quot;&gt;out there&lt;&#x2F;a&gt;. The nice thing about this setup is that &amp;quot;coc-java&amp;quot; installs the underlying LSP (a small piece of Eclipse) for you and configures everything. The other thing I like about CoC compared to the nvim-lspconfig project is that CoC allows me to configure key bindings once for all LSPs.&lt;&#x2F;p&gt;
&lt;p&gt;Enough blathering, let&#x27;s do this!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-install-openjdk&quot;&gt;1. Install OpenJDK&lt;&#x2F;h3&gt;
&lt;p&gt;Since I&#x27;m on Ubuntu 18.04 (&amp;quot;Bionic&amp;quot;), the most recent JDK available was 11, but really anything greater than Java 8 would have sufficed:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo apt-get install&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -y&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; openjdk-11-jdk
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The coc-java plugin will need to know where this is. I chose to tell the plugin like so:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; -xU JDK_HOME &#x2F;usr&#x2F;lib&#x2F;jvm&#x2F;java-1.11.0-openjdk-amd64
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;Yes, that&#x27;s fish shell, not bash. It&#x27;s the equivalent of adding &lt;code&gt;export JDK_HOME=...&lt;&#x2F;code&gt; in my .bashrc and then sourcing immediately.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-install-coc-nvim-plugin&quot;&gt;2. Install coc.nvim plugin&lt;&#x2F;h3&gt;
&lt;p&gt;Inside my ~&#x2F;.config&#x2F;nvim&#x2F;init.vim, I added this to install the plugin using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;junegunn&#x2F;vim-plug&quot;&gt;vim-plug&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-viml&quot; data-lang=&quot;viml&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;Plug &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;#39;neoclide&#x2F;coc.nvim&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, {&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;#39;branch&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;#39;release&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then restart Nvim and run &lt;code&gt;:PlugInstall&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-setup-keybindings&quot;&gt;3. Setup Keybindings&lt;&#x2F;h3&gt;
&lt;p&gt;The most interesting bits of functionality to me to set key bindings for in the init.vim I&#x27;ve captured below:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-viml&quot; data-lang=&quot;viml&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;nmap &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;silent&amp;gt; gd &amp;lt;Plug&amp;gt;(coc-definition)                               &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;quot; GoTo definition
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;nmap &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;silent&amp;gt; gr &amp;lt;Plug&amp;gt;(coc-references)                               &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;quot; GoTo references
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;nmap &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;leader&amp;gt;rn &amp;lt;Plug&amp;gt;(coc-rename)                                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;quot; Rename symbol
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;nnoremap &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;silent&amp;gt;&amp;lt;nowait&amp;gt; &amp;lt;space&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;  :&amp;lt;C-u&amp;gt;CocList diagnostics&amp;lt;cr&amp;gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;quot; Show all errors&#x2F;warnings
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;nnoremap &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;silent&amp;gt;&amp;lt;nowait&amp;gt; &amp;lt;space&amp;gt;c  :&amp;lt;C-u&amp;gt;CocList commands&amp;lt;cr&amp;gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;quot; Kitchen sink of all LSP commands
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;nnoremap &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;silent&amp;gt;&amp;lt;nowait&amp;gt; &amp;lt;space&amp;gt;&amp;lt;S-s&amp;gt;  :&amp;lt;C-u&amp;gt;CocList -I symbols&amp;lt;cr&amp;gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;quot; Fuzzy search for ALL symbols
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;4-building-muscle-memory&quot;&gt;4. Building Muscle Memory&lt;&#x2F;h3&gt;
&lt;p&gt;Being a vi user is all about building habits so that we can achieve various actions reflexively. For me that means picking a small number of keybindings and practicing them intentionally.&lt;&#x2F;p&gt;
&lt;p&gt;One new thing I&#x27;m trying is to write down the above keybindings on a piece of paper taped to my monitor. My hope is that this will help me memorize these shortcuts a bit more &amp;quot;organically&amp;quot; and I can eventually throw the paper away or replace with new bindings I want to start memorizing.&lt;&#x2F;p&gt;
&lt;p&gt;Every 6-12 months, I&#x27;ll weed out keybindings that didn&#x27;t stick. Here&#x27;s hoping that most of these keybindings are here for the long haul!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Simple Logging Façade for Java (slf4j) - Java Basics</title>
		<published>2021-07-12T00:00:00+00:00</published>
		<updated>2021-07-12T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/slf4j/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/slf4j/</id>
		<content type="html">&lt;p&gt;I&#x27;ve recently started on a new project where I need to write nontrivial amounts of Java, which is great! Except that I&#x27;ve never written any Java before, outside some data structure classes. So I&#x27;m going to try help myself retain and deepen my knowledge of various Java topics (beyond just syntax &amp;amp; semantics) by keeping short notes of a few paragraphs here on my blog to help me feel like I&#x27;m making progress.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;simple-logging-facade-for-java-slf4j&quot;&gt;Simple Logging Façade for Java (slf4j)&lt;&#x2F;h2&gt;
&lt;p&gt;Basically, the idea is to decouple the logging implementation from the log statements in your application until deploy time. At deploy time, you just add a Jar with the logging implementation to your &lt;code&gt;$CLASSPATH&lt;&#x2F;code&gt;, and the logging works. Or, if the Ops team wants to use a different logging framework, then they just swap in a new Jar in production and you the developer don&#x27;t have to change anything. It just works. That&#x27;s cool!&lt;&#x2F;p&gt;
&lt;p&gt;Rust has standardized on a similar logging framework (probably inspired by slf4j) which delegates the logging implementation to compile time. However, switching loggin implementations in Rust would require recompiling the entire application and would require &lt;em&gt;at least&lt;&#x2F;em&gt; one line of code change in the Cargo.toml. Win for slf4j on the deployment side!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adding-slf4j-to-your-java-code&quot;&gt;Adding slf4j to your Java Code&lt;&#x2F;h2&gt;
&lt;p&gt;There&#x27;s a few different patterns here, the log format string (like Python):&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Java
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;logger.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Something went wrong: {}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;, badThing);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-py&quot; data-lang=&quot;py&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Python
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;logger.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Something went wrong: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;, bad_thing);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These format strings are kinda the same as Rust too:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rs&quot; data-lang=&quot;rs&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Rust
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;info!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Something went wrong {}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;, bad_displayable_thing);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Another style is the fluent logging that basically uses the &amp;quot;builder&amp;quot; pattern to progressively construct a log message piece by piece:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;logger.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;atDebug&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;addArgument&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(myPojo).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;addArgument&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(dbConnection).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;addArgument&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Hi Mom!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Args: 1={} 2={} 3={}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can pass virtually anything to &lt;code&gt;addArgument()&lt;&#x2F;code&gt; and it will be coerced to a printable representation. You must use the &lt;code&gt;at&amp;lt;LEVEL&amp;gt;()&lt;&#x2F;code&gt; methods of &lt;code&gt;Logger&lt;&#x2F;code&gt; to use the fluent API, and &lt;code&gt;.log()&lt;&#x2F;code&gt; to flush it.&lt;&#x2F;p&gt;
&lt;p&gt;Copy-pasting from the &lt;a href=&quot;http:&#x2F;&#x2F;www.slf4j.org&#x2F;manual.html#fluent&quot;&gt;docs&lt;&#x2F;a&gt;, you can also label an argument with a key instead of only a position. By default, these two log statements will output the same string:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; using classical API
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;logger.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;debug&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;oldT={} newT={} Temperature changed.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;, newT, oldT);

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; using fluent API
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;logger.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;atDebug&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;addKeyValue&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;oldT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;, oldT).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;addKeyValue&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;newT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;, newT).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Temperature changed.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;creating-the-slf4j-logger-instance&quot;&gt;Creating the slf4j logger instance&lt;&#x2F;h3&gt;
&lt;p&gt;Since we don&#x27;t know during development what the logging implementation will be, we need to make this a runtime decision by the slf4j framework. That means using a factory. Let&#x27;s see it in action:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;org.slf4j.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Logger&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;org.slf4j.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;LoggerFactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;HelloWorld &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public static void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;) {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Logger&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt; logger &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;LoggerFactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;getLogger&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;HelloWorld&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
    logger.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Hello World&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
  }
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Logger&lt;&#x2F;code&gt; - this is the parent class that all logging implementation classes will subclass. It&#x27;s the actual logging object you call &lt;code&gt;.log()&lt;&#x2F;code&gt; on&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;LoggerFactory&lt;&#x2F;code&gt; - this is how slf4j figures out at runtime which implementation to give you&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It&#x27;s often handy to just have an instance of &lt;code&gt;Logger&lt;&#x2F;code&gt; ready to use, so in at least one codebase at work, I&#x27;ve seen instances of setting up the logger as a static member of the class, something like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;org.slf4j.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Logger&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;org.slf4j.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;LoggerFactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;HelloWorld &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;{
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;private static final &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Logger &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;logger &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;LoggerFactory&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;getLogger&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;HelloWorld&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);

  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;public static void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;args&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;) {
    logger.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Hello World&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;);
  }
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, we don&#x27;t have to pass loggers around or make sure each method creates a logger. Instead, every method on this class will now have access to a logger at any time.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Farewell to Mail-In-A-Box</title>
		<published>2021-05-01T00:00:00+00:00</published>
		<updated>2021-05-01T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/farewell-mailinabox/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/farewell-mailinabox/</id>
		<content type="html">&lt;p&gt;I&#x27;ve been using Mail-In-A-Box for quite some time now, and while it&#x27;s served me well for quite a while, I feel like it&#x27;s time to get more involved in managing my personal infra.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gratitude-for-mail-in-a-box&quot;&gt;Gratitude for Mail-in-a-box&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;joshdata.me&#x2F;&quot;&gt;Joshua Tauberer&lt;&#x2F;a&gt; has done an incredible job of making a self-hosted email server affordable and accessible, updating the project to incoporate the latest protocols and generally inspiring confidence that upgrades will JustWork(tm). He&#x27;s been incredibly generous with his time, and I doubt I would have gone down this path without the high quality of work he&#x27;s painstakingly lavished on this project for so many years. In many ways, he represents the finest traditions of open source.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;things-i-dislike-about-mail-in-a-box&quot;&gt;Things I Dislike about Mail-in-a-box&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;TLS problems&lt;&#x2F;strong&gt; Maybe it&#x27;s just me, but I can&#x27;t seem to resolve this &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mail-in-a-box&#x2F;mailinabox&#x2F;issues&#x2F;1425&quot;&gt;symlink loop issue&lt;&#x2F;a&gt; that takes down my server either every 24hrs or every 3 months, depending. Newer versions of Mail-in-a-box use &lt;a href=&quot;https:&#x2F;&#x2F;www.hardenize.com&#x2F;blog&#x2F;mta-sts&quot;&gt;MTA-STS&lt;&#x2F;a&gt; which means that I &lt;em&gt;must&lt;&#x2F;em&gt; fix TLS issues immediately or I can&#x27;t even read already delivered email messages. Once you combine that with &lt;a href=&quot;https:&#x2F;&#x2F;letsencrypt.org&#x2F;docs&#x2F;rate-limits&#x2F;&quot;&gt;rate limits at LetsEncrypt&lt;&#x2F;a&gt;, you have an amazing recipe for chronic downtime.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Limited tests&lt;&#x2F;strong&gt; and an ambivalent attitude towards &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mail-in-a-box&#x2F;mailinabox&#x2F;issues&#x2F;777#issuecomment-779423026&quot;&gt;continuous integration&lt;&#x2F;a&gt; means I can&#x27;t contribute back proof that my problem is fixed while keeping the existing functionality working. Also, it just means maintainers spend a ton of energy manually testing everything.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Email usability problems&lt;&#x2F;strong&gt; There are 2 big problems here: (1) addresses are only auto-suggested during email authoring if they are manually added to the address book, and (2) the calendar integration isn&#x27;t google calendar friendly. Specifically, if someone sends me an invite to an event, and I click &amp;quot;yes&amp;quot;, the event won&#x27;t appear on my calendar. This makes it hard to coordinate with others.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Backup and Monitoring&lt;&#x2F;strong&gt; The builtin monitoring tool, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;munin-monitoring&#x2F;munin&quot;&gt;Munin&lt;&#x2F;a&gt; can&#x27;t be configured to send alerts without forking the project or hacking the code. Personally, I&#x27;ve not worked with Munin much in my day job and would rather use something like telegraf + influxcloud, or grafana cloud, etc. Additionally, it could just be me, but I&#x27;ve never been able to get duplicity working and so I effectively have no backups.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Unconfigurable&lt;&#x2F;strong&gt; The project is very upfront about this:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please note that the goal of this project is to provide a simple, turn-key solution. There are basically no configuration options and you can’t tweak the machine’s configuration files after installation.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is a great way to structure a project to maximize the chances someone can install and use this software, even as a novice to the world of email infrastructure (like myself). Its very beginner friendly and it minimizes differences between users so that the commmunity can help each other as much as possible.&lt;&#x2F;p&gt;
&lt;p&gt;But for me as a professional software engineer, it&#x27;s limiting to have a rich ecosystem of plugins for Roundcube or other applications just out of reach.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Monolithic Architecture&lt;&#x2F;strong&gt; Because Mail-in-a-box tries to completely takeover and run self-contained and unconfigurable on a single VM, everything about it becomes a &lt;a href=&quot;https:&#x2F;&#x2F;avinetworks.com&#x2F;glossary&#x2F;single-point-of-failure&#x2F;&quot;&gt;spof&lt;&#x2F;a&gt;. Now, your DNS server is also your monitoring endpoint, your LDAP, your webmail client, and your email server. You can&#x27;t use tools like Kubernetes to do bin packing and run other things on the box. You also can&#x27;t leverage your experience with terraform or ansible to configure things.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;things-i-love-about-mail-in-a-box&quot;&gt;Things I love about Mail-in-a-box&lt;&#x2F;h2&gt;
&lt;p&gt;The builtin status checks are incredible! The forums and community is really active. All various applications fit nicely together. Upgrading Mail-in-a-Box generally means pulling in new features, bugfixes, and security enhancements without much effort on my part.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-new-kind-of-mail-in-a-box&quot;&gt;A new kind of Mail-in-a-box&lt;&#x2F;h2&gt;
&lt;p&gt;So, what if we wanted to make a personal email server that modular instead monolithic? What if we created a series of containers that could be deployed together in a known-good configuration, or extracted individually and reused in other projects? What if such a project had behavioral tests and a modern CI pipeline? That would be a project I&#x27;d be proud to put my name on.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;proposed-architecture&quot;&gt;Proposed Architecture&lt;&#x2F;h2&gt;
&lt;p&gt;Since we&#x27;re looking at supporting IMAP and SMTP protocols, we&#x27;ll need a layer-4 load-balancer&#x2F;reverse proxy&#x2F;gateway thingie. SMTP and IMAP protocols have a kind of banter going on, where clients and servers send one or more lines to each other over a persistent TCP connection. If bots could chat, this would basically be that kind of protocol.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;C: &amp;lt;open connection&amp;gt;
S:   * OK IMAP4rev1 Service Ready
C:   a001 login mrc secret
S:   a001 OK LOGIN completed
C:   a002 select inbox
S:   * 18 EXISTS
S:   * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
S:   * 2 RECENT
S:   * OK [UNSEEN 17] Message 17 is the first unseen message
S:   * OK [UIDVALIDITY 3857529045] UIDs valid
S:   a002 OK [READ-WRITE] SELECT completed
C:   a003 logout
S:   * BYE IMAP4rev1 server terminating connection
S:   a003 OK LOGOUT completed
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since managing LetsEncrypt via Mail-In-A-Box has a been weak spot for me, ideally we&#x27;d use ZeroSSL (another free ACME server with higher rate limits). Even better, let&#x27;s do TLS termination at the load balancer and separate encryption concerns from IMAP&#x2F;SMTP stuff.&lt;&#x2F;p&gt;
&lt;p&gt;To keep things modular and friendly to clustering solutions, we&#x27;ll want to manage sets of stateless docker containers separately for SMTP and IMAP bits and bobs, have a service discovery solution that integrates with the load balancer, let the load balancer manage the TLS certs, and some kind of cluster manager schedule the SMTP and IMAP workloads. We also need a stateful store of email data (mbox? mail queue? things like that I think) and some way to specific users and their credentials. Finally, we want to make sure we can build and test the clusters ability to send&#x2F;receive email in a CI pipeline, and monitoring hooks to integrate with standard tools. Now we&#x27;re cooking!&lt;&#x2F;p&gt;
&lt;!-- Diagram of all of that --&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;&#x2F;h2&gt;
&lt;p&gt;My plan is to write up my steps towards making this cluster a reality! Next stop, get postfix working.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Review of Diagrams-as-Code for mdBook</title>
		<published>2020-07-03T00:00:00+00:00</published>
		<updated>2020-07-03T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/markdown-diagrams/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/markdown-diagrams/</id>
		<content type="html">&lt;p&gt;When maintaining documentation for a software project, you need to keep diagrams to describe everything. However, those diagrams will go out of date fast if they aren&#x27;t easy to maintain. Rather than keep the raw diagram originals in a separate file, embedding diagrams inline using some other diagramming language (like dotviz) can make it easy to change the words and diagrams together and ease the maintenance burden. These days there are a lot of tools in this space. This post is an attempt to try out and compare these different tools strengths and weaknesses.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;criteria-for-tool-selection&quot;&gt;Criteria for Tool Selection&lt;&#x2F;h2&gt;
&lt;p&gt;For this post, I&#x27;m specifically interested in tools that can integrate with the mdBook static site generators and enable the author to embed the diagram code inline with the markdown text. There are lots of other tools that allow authoring a diagram in code &lt;em&gt;separately&lt;&#x2F;em&gt; from your markdown, but I&#x27;m not interested in those for this post.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve fallen in love with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;mdbook&quot;&gt;mdBook&lt;&#x2F;a&gt; for general purpose documentation. There&#x27;s basically nothing to configure and you get:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a static site generator&lt;&#x2F;li&gt;
&lt;li&gt;automatic, offline, fast search&lt;&#x2F;li&gt;
&lt;li&gt;automatic reloading while writing &amp;amp; editors&lt;&#x2F;li&gt;
&lt;li&gt;statically compiled binary&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Additionally, mdBook supports plugins which can add in support for all kinds of goodies, not least diagrams as code inline in your markdown.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-contenders-diagram-as-code-tools&quot;&gt;The Contenders: Diagram as Code Tools&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s the top tools I&#x27;m considering:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;badboy&#x2F;mdbook-mermaid&quot;&gt;mermaidjs&lt;&#x2F;a&gt; - v0.4.2&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sytsereitsma&#x2F;mdbook-plantuml&quot;&gt;plantuml&lt;&#x2F;a&gt; - v0.5.0&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;fzzr-&#x2F;mdbook-svgbob&quot;&gt;svgbob&lt;&#x2F;a&gt; - v0.1.1&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;mdbook-graphviz&quot;&gt;graphviz&lt;&#x2F;a&gt; - v0.0.2&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;preprocessor&lt;&#x2F;th&gt;&lt;th&gt;binary size&lt;&#x2F;th&gt;&lt;th&gt;compile time&lt;&#x2F;th&gt;&lt;th&gt;precompiled&lt;&#x2F;th&gt;&lt;th&gt;additional requirements&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;mdbook-plantuml&lt;&#x2F;td&gt;&lt;td&gt;5.8MB&lt;&#x2F;td&gt;&lt;td&gt;8m45s&lt;&#x2F;td&gt;&lt;td&gt;windows&lt;&#x2F;td&gt;&lt;td&gt;plantuml server&#x2F;executable&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;mdbook-mermaid&lt;&#x2F;td&gt;&lt;td&gt;4.3MB&lt;&#x2F;td&gt;&lt;td&gt;8m03s&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;none&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;mdbook-svgbob&lt;&#x2F;td&gt;&lt;td&gt;3.3MMB&lt;&#x2F;td&gt;&lt;td&gt;7m44s&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;none&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;mdbook-graphviz&lt;&#x2F;td&gt;&lt;td&gt;2.5MB&lt;&#x2F;td&gt;&lt;td&gt;6m55s&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;graphviz&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;em&gt;These compile times are very unscientific. Some crates were downloaded from the network and others were cached locally. Don&#x27;t read too much into that, except that if you&#x27;re going to build your mdbook in a CI pipeline, you&#x27;ll want to leverage the cache you don&#x27;t need to recompile these dependencies on every run.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The winner here is mdbook-mermaid because one can forgo the compile time and simply download a pre-compiled binary from the Github releases page for a wide variety of platforms and because it doesn&#x27;t require any additional install steps.&lt;&#x2F;p&gt;
&lt;p&gt;Mdbook-svgbob comes in second place over-all for compiling its image rendered inside itself, and not needing any other dependencies.&lt;&#x2F;p&gt;
&lt;p&gt;The PlantUML preprocessor is interesting because it can be configured to connect to a local or remote plantuml server. The advantage here would be if your team already has access to a plantuml server, you wouldn&#x27;t need to set up the plantuml server yourself.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;improvements-for-compile-time-file-size&quot;&gt;Improvements for Compile-Time &amp;amp; File Size&lt;&#x2F;h3&gt;
&lt;p&gt;All four crates depend on &lt;code&gt;clap&lt;&#x2F;code&gt;, &lt;code&gt;serde&lt;&#x2F;code&gt;, &lt;code&gt;pull-down-cmark&lt;&#x2F;code&gt;, &lt;code&gt;pull-down-cmark-to-cmark&lt;&#x2F;code&gt; and &lt;code&gt;mdbook&lt;&#x2F;code&gt;, mostly because this is the &lt;a href=&quot;https:&#x2F;&#x2F;rust-lang.github.io&#x2F;mdBook&#x2F;for_developers&#x2F;preprocessors.html#hints-for-implementing-a-preprocessor&quot;&gt;recommended approach&lt;&#x2F;a&gt; in the mdbook user guide chapter on preprocessors:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;By pulling in mdbook as a library, preprocessors can have access to the existing infrastructure for dealing with books... Then each chapter of the Book can be mutated in-place via Book::for_each_mut(), and then written to stdout with the serde_json crate... The pulldown-cmark crate implements a production-quality event-based Markdown parser, with the pulldown-cmark-to-cmark allowing you to translate events back into markdown text.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;While these dependencies are high quality, there are some smaller crates that would suffice for this use case:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;miniserde&quot;&gt;miniserde&lt;&#x2F;a&gt; instead of serde + serde_json&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;pico-args&quot;&gt;pico-args&lt;&#x2F;a&gt; instead of clap&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If mdbook itself exposed a small &amp;quot;core&amp;quot; crate that simply exported the &lt;code&gt;Preprocessor&lt;&#x2F;code&gt; trait and other types, that might reduce the file-size&#x2F;compilation time of all of these crates as well.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-images-are-produced&quot;&gt;How Images Are Produced&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;preprocessor&lt;&#x2F;th&gt;&lt;th&gt;format&lt;&#x2F;th&gt;&lt;th&gt;when&lt;&#x2F;th&gt;&lt;th&gt;separate files crated&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;mdbook-mermaid&lt;&#x2F;td&gt;&lt;td&gt;svg&lt;&#x2F;td&gt;&lt;td&gt;browser javascript&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;mdbook-svgbob&lt;&#x2F;td&gt;&lt;td&gt;svg&lt;&#x2F;td&gt;&lt;td&gt;mdbook build&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;mdbook-plantuml&lt;&#x2F;td&gt;&lt;td&gt;svg or png&lt;&#x2F;td&gt;&lt;td&gt;mdbook build&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;mdbook-graphviz&lt;&#x2F;td&gt;&lt;td&gt;svg&lt;&#x2F;td&gt;&lt;td&gt;mdbook build&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;From a setup and publishing point-of-view, mdbook-mermaid is probably in first place here. The rendered is simply copying your MermaidJS diagram from the code fence into a &lt;code&gt;&amp;lt;pre class=&amp;quot;mermaid&amp;quot;&amp;gt;{}&amp;lt;&#x2F;pre&amp;gt;&lt;&#x2F;code&gt; html tag, and defering the SVG rendering until page load time. So, theoretically, this processor should have the smallest impact on mdbook build time. (Of course, this would make your readers page-load time worse, but that assumes anyone&#x27;s reading your pages anyway!)&lt;&#x2F;p&gt;
&lt;p&gt;Having separate SVG files created is a slight win here. You can send someone a URL to this image, whereas if the image only exists inline with the HTML, its slighltly more difficult to get access to.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;breadth-of-diagram-styles&quot;&gt;Breadth of Diagram Styles&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;preprocessor&lt;&#x2F;th&gt;&lt;th&gt;styling&lt;&#x2F;th&gt;&lt;th&gt;flow chart&lt;&#x2F;th&gt;&lt;th&gt;graphs&lt;&#x2F;th&gt;&lt;th&gt;sequence diagram&lt;&#x2F;th&gt;&lt;th&gt;class diagram&lt;&#x2F;th&gt;&lt;th&gt;cloud&lt;&#x2F;th&gt;&lt;th&gt;SQL schema&lt;&#x2F;th&gt;&lt;th&gt;shapes&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;mdbook-svgbob&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;probably&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;probably&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;mdbook-mermaid&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;some&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;mdbook-plantuml&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;mdbook-graphviz&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;probably&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;no?&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Honestly, this is just hard to compare. svgbob tries to infer outlines of shapes from ascii art, so it &lt;em&gt;can&lt;&#x2F;em&gt; create just about anything, but it requires the writer to be willing to play with random characters as an art medium, instead of as a rigidly defined illustration protocol. The big upshot of this is that even when the svgbob illustrations are viewed as plain text, they should convey the same impression. The downside is diagrams produced this way are likely much more time consuming to update and maintain.&lt;&#x2F;p&gt;
&lt;p&gt;MermaidJS seems to just steal whole swathes of syntax and semantics from graphviz, but offers just a subset of that functionality. Then it adds many other forms of visualization that I don&#x27;t believe are part of graphviz (like pie charts). PlantUML is a newish dialect of UML designed to be used in documentation. It supports many different forms of visualization. Despite the code not looking much like the final image, graphviz, plantuml, and mermaid make it relatively easy to refactor your image by tweaking the code behind it compared to svgbob. If suddenly you need to add (or remove) a box from the middle of your diagram, or decide to start using squares everywhere, you&#x27;ll have retype nearly the whole svgbob diagram -- but probably not for the others.&lt;&#x2F;p&gt;
&lt;p&gt;The mdbook-graphviz only supports the &amp;quot;dot&amp;quot; visualizations, so puts the plantuml and mermaid preprocessors ahead in this category.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prettyness-factor&quot;&gt;Prettyness Factor&lt;&#x2F;h2&gt;
&lt;p&gt;I have to say, the svgbob diagrams seem to be the most beautiful, and PlantUML to be the ugliest. Mermaid comes in second place to me based only my personal taste. YMMV.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;svgbob diagrams&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2020&#x2F;svgbob-pretty.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;MermaidJS Sequence Diagram&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2020&#x2F;mermaidjs-pretty.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;GraphViz linux diagram&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2020&#x2F;graphviz-pretty.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;PlantUML sequence diagram&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2020&#x2F;plantuml-pretty.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ecosystem&quot;&gt;Ecosystem&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2020&#x2F;diagram-tools-google-trends.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Interestingly enough, PlantUML is implemented &lt;em&gt;on top of&lt;&#x2F;em&gt; GraphViz, which was released in 1991 by AT&amp;amp;T Labs. So, there&#x27;s plenty of examples, tutorials, and chatrooms where you can find support for both of these tools.&lt;&#x2F;p&gt;
&lt;p&gt;Despite the rather meager search trends for MermaidJS compared to the top two, there have been two main contributors over the life of the project (since 2014) and there is a Gitter chatroom. Many developers have contributed tens of commits to the project over its life. Just in the last week, 16 pull requests were merged. Also, mermaidjs has an awesome &lt;a href=&quot;https:&#x2F;&#x2F;mermaid-js.github.io&#x2F;mermaid-live-editor&#x2F;&quot;&gt;live code editor&lt;&#x2F;a&gt; you can play with. Highly recommend!&lt;&#x2F;p&gt;
&lt;p&gt;By comparison, svgbob really has only 1 developer (with about 10 others who&#x27;ve contributed a few commits a piece) and no activity in the last month. It might be tough to get support when you encounter rough edges here. Despite these worrying signs, there is a &lt;a href=&quot;https:&#x2F;&#x2F;ivanceras.github.io&#x2F;svgbob-editor&#x2F;&quot;&gt;nice code editor&lt;&#x2F;a&gt; here as well.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Personally, I&#x27;ll be using MermaidJS with mdbook going forward, but I&#x27;ll definitely play with svgbob as well!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Returning Trait Objects</title>
		<published>2020-01-22T00:00:00+00:00</published>
		<updated>2020-01-22T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/returning-trait-objects/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/returning-trait-objects/</id>
		<content type="html">&lt;p&gt;This is a fairly basic Rust syntax issue that I&#x27;ve run into several times. Based on unknowable runtime conditions, I will return one of several different return types from the same function. Also, this return type must use methods from two traits. How can I express this in Rust?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;solution&quot;&gt;Solution&lt;&#x2F;h2&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rs&quot; data-lang=&quot;rs&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;runtime_condition&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) -&amp;gt; Box&amp;lt;dyn BoilerTrait&amp;gt; {
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; runtime_condition {
    Box::new(Type1)
  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
    Box::new(Type2)
  }
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BoilerTrait&lt;&#x2F;code&gt; is a trait that requires the traits I actually care about (ex: &lt;code&gt;UsefulTrait&lt;&#x2F;code&gt; and &lt;code&gt;Debug&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Type1&lt;&#x2F;code&gt; and &lt;code&gt;Type2&lt;&#x2F;code&gt; are concrete types that both implement trait &lt;code&gt;BoilerTrait&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;I can use methods from any of the traits required by &lt;code&gt;BoilerTrait&lt;&#x2F;code&gt; on return value of &lt;code&gt;foo()&lt;&#x2F;code&gt; transparently&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;failure-1-return-impl-trait&quot;&gt;Failure 1: return impl Trait&lt;&#x2F;h2&gt;
&lt;p&gt;I mistakenly thought impl Trait in return position would be the way to handle this case. Here&#x27;s code that does &lt;strong&gt;NOT&lt;&#x2F;strong&gt; compile:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rs&quot; data-lang=&quot;rs&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;runtime_condition&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) -&amp;gt; impl BoilerTrait {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; runtime_condition {
        Box::new(Type1)
    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
        Box::new(Type2)
    }
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The compiler complains:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rs&quot; data-lang=&quot;rs&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;error[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;E0308&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;]: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; and &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; have incompatible types
...
   = note: expected &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; `std::boxed::Box&amp;lt;Type1&amp;gt;`
              found &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; `std::boxed::Box&amp;lt;Type2&amp;gt;`
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For the return impl Trait syntax, the compiler will only allow us to return &lt;strong&gt;&lt;em&gt;one&lt;&#x2F;em&gt;&lt;&#x2F;strong&gt; concrete type.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-can-there-be-only-one&quot;&gt;Why Can There Be Only One?&lt;&#x2F;h3&gt;
&lt;p&gt;I &lt;em&gt;think&lt;&#x2F;em&gt; there are two advantages if I understand this right:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Static dispatch and runtime performance improvements&lt;&#x2F;li&gt;
&lt;li&gt;More flexible public API contract for consumers of a library&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;It&#x27;s a middleground for library authors to avoid committing to a specific type in their public API without sacrificing the performance improvements of static dispatch. I &lt;em&gt;think&lt;&#x2F;em&gt; you might even be able to swap out the return type at compile time using &lt;code&gt;cfg!()&lt;&#x2F;code&gt; macros or what-not. Cool!&lt;&#x2F;p&gt;
&lt;p&gt;But, that&#x27;s not my use case.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;failure-2-multi-trait-object&quot;&gt;Failure 2: Multi-Trait Object&lt;&#x2F;h2&gt;
&lt;p&gt;More code that doesn&#x27;t quite compile:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rs&quot; data-lang=&quot;rs&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;runtime_condition&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) -&amp;gt; Box&amp;lt;dyn UsefulTrait+Debug&amp;gt; {
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; runtime_condition {
    Box::new(Type1)
  } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
    Box::new(Type2)
  }
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The compiler tells us:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rs&quot; data-lang=&quot;rs&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;error[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;E0225&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;]: only auto traits can be used as additional traits in a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;trait &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;object
  --&amp;gt; src&#x2F;main.rs:12:33
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I have no idea why you can&#x27;t name multiple normal traits in a trait object. That&#x27;s why we have to make a super trait &lt;code&gt;BoilerTrait&lt;&#x2F;code&gt; that requires the traits you actually want.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;use-type-annotations-for-temporaries&quot;&gt;Use Type Annotations for Temporaries&lt;&#x2F;h2&gt;
&lt;p&gt;When trying something more complicated than the examples above, temporary values weren&#x27;t automatically recognized as the trait object I had in mind...It seems like the type inference &lt;em&gt;could&lt;&#x2F;em&gt; be better at this case, but in rust 1.40.0 you need explicit type annotations for this case.&lt;&#x2F;p&gt;
&lt;p&gt;First, the code that does &lt;strong&gt;NOT&lt;&#x2F;strong&gt; compile&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rs&quot; data-lang=&quot;rs&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;runtime_condition&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) -&amp;gt; Box&amp;lt;dyn BoilerTrait&amp;gt; {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; temporary = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; runtime_condition {
        Box::new(Type1)
    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
        Box::new(Type2)
    };
    temporary
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The compiler complains that the conditional returns different types. This is confusing because, the exact same expressions were returned just fine a moment ago without the let binding.&lt;&#x2F;p&gt;
&lt;p&gt;In our previous examples, without the temporary, the final expression from the conditional is returned from the function, which is explicitly annotated as &lt;code&gt;-&amp;gt; Box&amp;lt;dyn BoilerTrait&amp;gt;&lt;&#x2F;code&gt; so the compiler doesn&#x27;t have to do any guessing about the type. However, it does have to guess about the type of &lt;code&gt;temporary&lt;&#x2F;code&gt; because there&#x27;s no type annotation when the value is bound. Once the compiler decided that &lt;code&gt;temporary&lt;&#x2F;code&gt; is going to be a concrete type, we&#x27;ve lost the trait object.&lt;&#x2F;p&gt;
&lt;p&gt;The solution is easy, just add type annotation to the let binding:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rs&quot; data-lang=&quot;rs&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; temporary: Box&amp;lt;dyn BoilerTrait&amp;gt; = ...;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;full-solution&quot;&gt;Full Solution&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s a fuller working code example and a &lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;?version=stable&amp;amp;mode=debug&amp;amp;edition=2018&amp;amp;gist=8d0c2f75a87f037c2c71fa6df676e9d9&quot;&gt;playground&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rs&quot; data-lang=&quot;rs&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; useful trait 1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;std::fmt::Debug;

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; useful trait 2
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;trait &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;UsefulTrait {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;useful&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {}
}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;trait &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;BoilerTrait:UsefulTrait + Debug {}

#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(Debug)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;Type1;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;UsefulTrait &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;Type1 {}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;BoilerTrait &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;Type1 {}

#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;derive&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(Debug)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;Type2;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;UsefulTrait &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;Type2 {}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;impl &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;BoilerTrait &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;Type2 {}

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;runtime_condition&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) -&amp;gt; Box&amp;lt;dyn BoilerTrait&amp;gt; {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; temporary: Box&amp;lt;dyn BoilerTrait&amp;gt; = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; runtime_condition {
        Box::new(Type1)
    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
        Box::new(Type2)
    };
    temporary
}

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Call method from trait UsefulTrait
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;useful&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Call method from Debug trait
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;dbg!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;));
    dbg!(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;foo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;));
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</content>
	</entry>
	<entry xml:lang="en">
		<title>Tracking Personal Finances through Email</title>
		<published>2019-11-22T00:00:00+00:00</published>
		<updated>2019-11-22T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/tracking-personal-finance-through-email/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/tracking-personal-finance-through-email/</id>
		<content type="html">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.mint.com&#x2F;&quot;&gt;Mint&lt;&#x2F;a&gt; redefined personal finances for me: automatic categorization, syncing with my bank, real time alerts, mobile and web apps. In the aftermath of the Equifax breach, I felt scared about having &lt;em&gt;all&lt;&#x2F;em&gt; my financial history in one place with millions of other people. It was time to take control of my financial data.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-little-background-on-me&quot;&gt;A Little Background on Me&lt;&#x2F;h2&gt;
&lt;p&gt;My income was beginning to feel stretched thin as my family grew from 2 to 4. Childcare costs climbed to 50% of our income, and we couldn&#x27;t save money. I feel strongly that Facebook, Google, and Amazon are undermining civil society, democracy and capitalism, so I doubted there was huge earning potential for me somewhere else. Oh, and, we wanted to remodel our house...Suddenly, I really needed a budget.&lt;&#x2F;p&gt;
&lt;p&gt;Spoiler alert: I &lt;em&gt;still&lt;&#x2F;em&gt; need a budget. Lots of blog posts on these topics explain magic solutions, but this post is an odyssey and confessional through all of my failed approaches to date. It&#x27;s 50% therapy, 30% competitve landscape, and maybe 20% technical problems and solutions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;&#x2F;h2&gt;
&lt;p&gt;Our ultimate goal was to formulate and follow a realistic budget to find enough money for our big expenses.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Requirements:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Download and classify transactions automatically&lt;&#x2F;li&gt;
&lt;li&gt;Help with spending:
&lt;ul&gt;
&lt;li&gt;Reactively -- alert about overspending&lt;&#x2F;li&gt;
&lt;li&gt;Proactively -- check remaining budget before buying&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Banks, credit cards, and retirement accounts&lt;&#x2F;li&gt;
&lt;li&gt;iPhone, Android, MacOS&lt;&#x2F;li&gt;
&lt;li&gt;No 3rd parties&lt;&#x2F;li&gt;
&lt;li&gt;Empower my non-tech life partner&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;After reading the NY Times article about &lt;a href=&quot;https:&#x2F;&#x2F;www.nytimes.com&#x2F;2018&#x2F;06&#x2F;23&#x2F;technology&#x2F;smart-home-devices-domestic-abuse.HTML&quot;&gt;technology-based domestic partner abuse&lt;&#x2F;a&gt;, I&#x27;ve started taking that last bullet point more seriously. Each of those stories boiled down to one partner setting up the smart home, and then post-breakup using aspects of the smart home to harrass the other person. So, if both partners are familiar and comfortable with all the important tech in their life, this abuse should &lt;em&gt;mostly&lt;&#x2F;em&gt; be mitigated. For personal finances, this means there must be a simple enough user interface accessible on both our smart phones.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Nice to haves:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Double-entry accounting&lt;&#x2F;li&gt;
&lt;li&gt;Export &#x2F; import to &lt;a href=&quot;https:&#x2F;&#x2F;plaintextaccounting.org&#x2F;&quot;&gt;ledger&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Good visualization&lt;&#x2F;li&gt;
&lt;li&gt;Customizable categorization -- an area where I wanted more than Mint provided&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;looking-at-alternatives&quot;&gt;Looking at Alternatives&lt;&#x2F;h2&gt;
&lt;p&gt;There&#x27;s loads of options out there. I only looked at open source options that let me own the data. That eliminated things like LunchMoney, YooNeedABudget, and other paid options.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ledger-hledger-etc&quot;&gt;ledger &#x2F; hledger &#x2F; etc&lt;&#x2F;h3&gt;
&lt;p&gt;I spent quite a bit of time tracking my finances with these tools, and they are incredible. I felt so on top of where my money was going when I used these. I&#x27;m convinced that double-entry accounting in ledger-style is what I want personally under-the-hood for personal finances. You can teach yourself a lot of accounting just from the documentation in these projects, and I&#x27;ve that helpful in my professional life and in various roles in my HOA and other civic groups that handle money.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;ledger-friends-pros&quot;&gt;Ledger &amp;amp; Friends Pros:&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;Double-entry accounting&lt;&#x2F;li&gt;
&lt;li&gt;No 3rd parties&lt;&#x2F;li&gt;
&lt;li&gt;Portable data format&lt;&#x2F;li&gt;
&lt;li&gt;Mature technology&lt;&#x2F;li&gt;
&lt;li&gt;Excellent documentation&lt;&#x2F;li&gt;
&lt;li&gt;Active community&lt;&#x2F;li&gt;
&lt;li&gt;Can support any currency or commodity&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;ledger-friends-cons&quot;&gt;Ledger &amp;amp; Friends Cons:&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;Can&#x27;t pull data from my bank &#x2F; credit cards -- manual step here&lt;&#x2F;li&gt;
&lt;li&gt;No real time alerts&lt;&#x2F;li&gt;
&lt;li&gt;Single device &#x2F; terminal or desktop website UIs&lt;&#x2F;li&gt;
&lt;li&gt;Limited categorization functionality&lt;&#x2F;li&gt;
&lt;li&gt;Can&#x27;t imagine my partner using this&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;gnucash&quot;&gt;GnuCash&lt;&#x2F;h3&gt;
&lt;p&gt;If you prefer &lt;a href=&quot;https:&#x2F;&#x2F;www.gimp.org&#x2F;&quot;&gt;GIMP&lt;&#x2F;a&gt; to Photoshop, you might prefer &lt;a href=&quot;https:&#x2F;&#x2F;www.gnucash.org&#x2F;features.phtml&quot;&gt;GnuCash&lt;&#x2F;a&gt; to Quicken. I haven&#x27;t used this tool extensively, but it seems like it has a community behind it, and it&#x27;s the only tool I looked at that can help with tax preparation. Folks running a small business are probably more the target market for this tool, rather than personal finance folks.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;gnucash-pros&quot;&gt;GnuCash Pros&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;Private - No Third Parties&lt;&#x2F;li&gt;
&lt;li&gt;Double Entry Accounting&lt;&#x2F;li&gt;
&lt;li&gt;Active development and community&lt;&#x2F;li&gt;
&lt;li&gt;Strong support for Quicken exports&lt;&#x2F;li&gt;
&lt;li&gt;Feature rich&lt;&#x2F;li&gt;
&lt;li&gt;Desktop GUI (and Android too)&lt;&#x2F;li&gt;
&lt;li&gt;Multi-currency&lt;&#x2F;li&gt;
&lt;li&gt;Internationalization&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;gnucash-cons&quot;&gt;GnuCash Cons&lt;&#x2F;h4&gt;
&lt;p&gt;Basically the same as ledger &amp;amp; friends...&lt;&#x2F;p&gt;
&lt;h3 id=&quot;firefly-iii-build-a-mint-clone-friends&quot;&gt;Firefly III &amp;amp; Build-a-Mint-Clone &amp;amp; Friends&lt;&#x2F;h3&gt;
&lt;p&gt;There&#x27;s a ton of tools like &lt;a href=&quot;https:&#x2F;&#x2F;docs.firefly-iii.org&#x2F;&quot;&gt;Firefly III&lt;&#x2F;a&gt;. Some of them are double entry accounting, others are not.  Many of them can import CSVs and Quickbook files. Some of these tools are desktop apps (not multi-device) and others have web interfaces. &lt;a href=&quot;https:&#x2F;&#x2F;beancount.github.io&#x2F;fava&#x2F;&quot;&gt;Fava&lt;&#x2F;a&gt; is the closest to what I want in this category. It can be accessed via a website (so that counts as multi-device) and it&#x27;s a near cousin to ledger.&lt;&#x2F;p&gt;
&lt;p&gt;None of these work for me because none of these tools can sync data from banks without using a 3rd party API. Also, I&#x27;m not sure I could get my partner to use any of them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;summary-of-alternatives&quot;&gt;Summary of Alternatives&lt;&#x2F;h3&gt;
&lt;p&gt;No alternatives had these three features at the same time:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic download of transactions&lt;&#x2F;li&gt;
&lt;li&gt;No third party data sharing&lt;&#x2F;li&gt;
&lt;li&gt;Multi-device&lt;&#x2F;li&gt;
&lt;li&gt;Compelling for my partner&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I felt like I had an opportunity here to solve my own problem and possibly bring a new product to the market.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dead-end-1-react-rethinkdb&quot;&gt;Dead End 1: React &amp;amp; RethinkDB&lt;&#x2F;h2&gt;
&lt;p&gt;Back in 2016, I started using &lt;a href=&quot;https:&#x2F;&#x2F;rethinkdb.com&#x2F;blog&#x2F;2.3.6-release&quot;&gt;RethinkDB&lt;&#x2F;a&gt; to store the transactions, and React to render charts and graphs about my transactions. I figured out how to import CSVs from one of my bank, and started calculating net income. This project was a lot of fun, but didn&#x27;t really solve the hardest problem at all: downloading financials automatically.&lt;&#x2F;p&gt;
&lt;p&gt;Around this time, RethinkDB Inc shutdown, and so I removed the backend and made this an entirely JS + HTML project without a server. I realized you could probably build a 100% offline client to handle this, if you were willing to make the user send you CSV files from the bank themselves.&lt;&#x2F;p&gt;
&lt;p&gt;My work and home life got very difficult, and I gave up for at least a year.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;learnings&quot;&gt;Learnings&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Client side JavaScript could probably do a lot of stuff I hadn&#x27;t consider before&lt;&#x2F;li&gt;
&lt;li&gt;Babel&#x2F;webpack&#x2F;frontend toolchains are the absolute worst for prototyping if you don&#x27;t regularly work on the frontend full time (hello &lt;a href=&quot;https:&#x2F;&#x2F;vuejs.org&#x2F;&quot;&gt;vue.js&lt;&#x2F;a&gt;!)&lt;&#x2F;li&gt;
&lt;li&gt;Downloading transactions should be the first problem I solve because everything else is probably easy&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;dead-end-2-curl&quot;&gt;Dead End 2: Curl&lt;&#x2F;h2&gt;
&lt;p&gt;There were awful &lt;a href=&quot;https:&#x2F;&#x2F;www.washingtonpost.com&#x2F;graphics&#x2F;2017&#x2F;national&#x2F;california-wildfires-comparison&#x2F;&quot;&gt;fires in California in 2017&lt;&#x2F;a&gt;, and then &lt;a href=&quot;https:&#x2F;&#x2F;www.vox.com&#x2F;2018&#x2F;11&#x2F;9&#x2F;18078916&#x2F;california-wildfires-2018-camp-woolsey-hill&quot;&gt;again in 2018&lt;&#x2F;a&gt; and our windows didn&#x27;t seal at all. I worried about exposing our children to &lt;a href=&quot;https:&#x2F;&#x2F;sf.curbed.com&#x2F;2018&#x2F;11&#x2F;15&#x2F;18096611&#x2F;air-quality-sf-epa-camp-fire-smoke&quot;&gt;Beijing-level air-quality&lt;&#x2F;a&gt; year after year, and so we wanted to replace our windows. The costs were staggering to me, and given our childcare costs, there was no way we could afford it without serious cost-cutting. I decided to try making a budgeting tool again.&lt;&#x2F;p&gt;
&lt;p&gt;Remembering that downloading transactions was the hardest thing, I start thinking about this first. I used the network tab in my browser to see how to login to the bank and download a CSV of transactions. Then I tried copying those commands as curl calls. I figured that the cookies would have all the details necessary to prove I was logged in and that the bank&#x27;s server software would see curl commands copied from my browser as equivalent to the real thing.&lt;&#x2F;p&gt;
&lt;p&gt;I was completely wrong. After many nights and weekends, I discovered the credit union used an ancient ASP server framework that serialized and hashed all of the session state, including initial session ids, inside various form elements on the page. Every time I clicked on a link or button inside my browser on their website, the client side code would take the form state from the previous request scattered across form elements, and then produce a new hashed response code in a mangled field name that varied, and return that to the server. Without this complex dance of tightly coupled client and server code, it was impossible to move through the UI. I could only find this information after a trip down the wayback machine when trying to identify the server framework identifying some cryptic fields being based to backend. So, the chances of me being able to find working documentation for the exact version of this ancient proprietary framework used by my local credit union were slim.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;learnings-1&quot;&gt;Learnings&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;My bank&#x27;s website might be slightly harder to hack than I first thought -- go bank!&lt;&#x2F;li&gt;
&lt;li&gt;I would need to use a real browser to login and download transactions&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;dead-end-3-selenium&quot;&gt;Dead End 3: Selenium&lt;&#x2F;h2&gt;
&lt;p&gt;I spent the next two or three weeks of 15min sessions writing up some &lt;a href=&quot;https:&#x2F;&#x2F;selenium.dev&#x2F;&quot;&gt;Selenium&lt;&#x2F;a&gt; scripts in Python to login to the bank and download transactions. Having solved this and run the code at least twice, I felt sure I had nailed this.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;port-to-rust&quot;&gt;Port to Rust&lt;&#x2F;h3&gt;
&lt;p&gt;I love &lt;a href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; a &lt;em&gt;lot&lt;&#x2F;em&gt; and wanted to spend my spare time writing software in Rust. So I started porting the Python to Rust. Unfortunately, &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;webdriver_client&quot;&gt;Rust libraries for Selenium&lt;&#x2F;a&gt; aren&#x27;t nearly as feature rich and mature (or ergonomic) as &lt;a href=&quot;https:&#x2F;&#x2F;pypi.org&#x2F;project&#x2F;selenium&#x2F;&quot;&gt;those in Python&lt;&#x2F;a&gt;. The maintainers I chatted with were super helpful and willing to accept PRs and feedback, and I was able to get something sort of working.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;challenges-with-selenium&quot;&gt;Challenges with Selenium&lt;&#x2F;h3&gt;
&lt;p&gt;Having now run these scripts over a tethered connection 5-10 times in a row per session (there were many sessions) and over wifi in various locations, I started hitting random failures. Sometimes, it was just timeouts for finding a link on the page were too low, but other times the bank would put another page in the login flow to announce a promotion (student loans or something) and my script would break 1 in 4 times. Sometimes at night or when tethered or running the script a lot, the bank would require an SMS second factor authentication. Score one for bank security noticing I was a bit odd for logging in over and over!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;state-machines&quot;&gt;State Machines&lt;&#x2F;h3&gt;
&lt;p&gt;After listening to a &lt;a href=&quot;https:&#x2F;&#x2F;www.pythonpodcast.com&#x2F;automat-state-machines-with-glyph-lefkowitz-episode-116&#x2F;&quot;&gt;podcast about the Automat library&lt;&#x2F;a&gt;, I realized that I could solve these problems with a state machine. I basically need to make a graph of state transitions, and iteratively complete a state transition (enter my username and click &amp;quot;login&amp;quot;) then detect the next state. (I&#x27;m not quite sure if this technically fits into the definition of a &amp;quot;state machine&amp;quot;, but its close.)&lt;&#x2F;p&gt;
&lt;p&gt;I realized I could solve the 2fa problem by connecting an &lt;a href=&quot;https:&#x2F;&#x2F;ifttt.com&#x2F;android_messages&quot;&gt;IFTTT SMS-trigger&lt;&#x2F;a&gt; on my phone to a &lt;a href=&quot;https:&#x2F;&#x2F;ifttt.com&#x2F;maker_webhooks&quot;&gt;webhook to my webapp&lt;&#x2F;a&gt;, and then parse out the text message for the one time passcode. This could be incorporated into the state machine.&lt;&#x2F;p&gt;
&lt;p&gt;It kinda worked! Sometimes, as long as I didn&#x27;t do the SMS thing. But I had several accounts with other financial institutions I wanted to scrape. I worried about how I could possibly find the mental health and time to write another version of this for every single bank out there.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;code-quality-issues&quot;&gt;Code Quality Issues&lt;&#x2F;h3&gt;
&lt;p&gt;My code was really hard to work with. I hacked it together when I was at my most tired, often forcing myself to code in the middle of the night or during my 20 minute bus ride to work. It was mostly a 1000 line function that panicked without real error messages when anything went wrong. Code was repeated all over the place and there were no tests.&lt;&#x2F;p&gt;
&lt;p&gt;To solve these problems, I spent many hours architecting the program. I tried to follow a &lt;a href=&quot;https:&#x2F;&#x2F;airbrake.io&#x2F;blog&#x2F;software-design&#x2F;domain-driven-design&quot;&gt;Domain Driven Design&lt;&#x2F;a&gt;, with various abstractions to represent Banks, Transactions, Categories and Budgets. There were RFC-style plans for everything I wanted to accomplish. But the more planning I did, the more dread and resentment I felt at trying to tackle such a large project without getting enough sleep or having anyone to help me, and trying to do so with a difficult programming language and small ecosystem.&lt;&#x2F;p&gt;
&lt;p&gt;I was just super unhappy with life due to work and family and neighbor stuff going on, and I gave up for many months.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;learnings-2&quot;&gt;Learnings&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;I needed to be more surgical in both planning and writing code&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.martinfowler.com&#x2F;bliki&#x2F;Yagni.html&quot;&gt;YAGNI&lt;&#x2F;a&gt; -- I spent too much time planning out a fully baked program when I hadn&#x27;t proven out the basic approach was scalable to multiple financial instutions&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;ve been at this for &lt;em&gt;years&lt;&#x2F;em&gt; already (no one on Hacker News seems to spend years doing anything), so I need to take time to relax and sleep and only do this when it&#x27;s fun&lt;&#x2F;li&gt;
&lt;li&gt;As in UX testing, Selenium can be really fragile, especially if screenshot support is missing from your library&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;current-not-yet-working-approach-email&quot;&gt;Current (Not-Yet-Working) Approach: Email&lt;&#x2F;h2&gt;
&lt;p&gt;At this point of burnout and frustration, I read several posts on Hacker News (which I can no longer find) about using transaction alert emails to track personal finances. Each of these blog posts focused on the simplicity of this approach (especially compared to Selenium), but no one wrote about the security implications.&lt;&#x2F;p&gt;
&lt;p&gt;Instead of entrusting your bank password to questionable browser hijacking code running unsupervised inside your bank account, you configure your bank to send emails about every transaction. Basically every bank supports this, so you don&#x27;t have to wait for banks to have proper APIs.&lt;&#x2F;p&gt;
&lt;p&gt;Assuming you setup a separate email address for this purpose with a different password than your bank account, then there&#x27;s nothing for a hacker to exploit. All they can do is read about your finances, not &lt;em&gt;steal all your money!&lt;&#x2F;em&gt; Also, Selenium can&#x27;t fuck up your bank account on accident either. Once I thought through the security, I completely gave up on Selenium.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;imap-mysql-grafana&quot;&gt;IMAP -&amp;gt; MySQL -&amp;gt; Grafana&lt;&#x2F;h3&gt;
&lt;p&gt;Learning from my recent burnout on this project, I decided to write the minimum amount of code as fast as possible. The best advice for this I&#x27;ve found is to reuse as much open source software as possible. Basically, I would write a script in Rust to check emails then parse them for transactions details, then write that information to MySQL. I could visualize that information using &lt;a href=&quot;https:&#x2F;&#x2F;grafana.com&#x2F;grafana&#x2F;&quot;&gt;Grafana&lt;&#x2F;a&gt;. No web server, no frontend code, very little math.&lt;&#x2F;p&gt;
&lt;p&gt;I got this working in under a month of nights and weekends. But as soon as I tried to visualize the data, it was clear that the data was wrong. Really wrong. I was pretty discouraged. I felt like Grafana wasn&#x27;t going to be a great option for building an easy interface for my partner to use. Given the data wasn&#x27;t right, I decided to remove Grafana and focus on cleaning the data.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;data-cleanup&quot;&gt;Data Cleanup&lt;&#x2F;h3&gt;
&lt;p&gt;Digging into the data, I noticed that any transfers between accounts were being double counted. So, that&#x27;s sort of a tricky problem. I still haven&#x27;t really solved that one yet. Instead, I decided to focus on one single account, and tackle cross-account issues once I was more confident in the basic data.&lt;&#x2F;p&gt;
&lt;p&gt;Looking into one account at a time, I started finding lots of problems:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of transactions were missing&lt;&#x2F;li&gt;
&lt;li&gt;There were &lt;em&gt;extra&lt;&#x2F;em&gt; transactions, with totally wrong amounts&lt;&#x2F;li&gt;
&lt;li&gt;All the dates were wrong&lt;&#x2F;li&gt;
&lt;li&gt;Some transactions were doubled&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This past month is the point were I&#x27;m finally starting to find really interesting details about how banks send emails. So, this is sign I&#x27;m possibly doing something right?&lt;&#x2F;p&gt;
&lt;h4 id=&quot;missing-transactions&quot;&gt;Missing Transactions&lt;&#x2F;h4&gt;
&lt;p&gt;My first pass at parsing emails involved very detailed regexes. These were often only right for some of the messages. Instead, I&#x27;ve taken to just writing regexes that use a lot of &lt;code&gt;blah blah .* blah blah&lt;&#x2F;code&gt; in them. This seems to work much better.&lt;&#x2F;p&gt;
&lt;p&gt;Also, multi-part emails kicked my ass for a long time. Like, there would be HTML and text versions of a message inside the same email. But also some messages from the same bank would straight up text or just HTML. So, I was finally able to unify the code to handle that by making a list of parts of an email (the list might only have one element), then iterating over the list and stripping any HTML tags out of HTML emails. Then I would take the first element from the list for parsing with my regexes. This seems to work great!&lt;&#x2F;p&gt;
&lt;h4 id=&quot;extra-totally-wrong-transactions&quot;&gt;Extra Totally Wrong Transactions&lt;&#x2F;h4&gt;
&lt;p&gt;Turns out these emails were actually messages about &lt;em&gt;declined&lt;&#x2F;em&gt; transactions. These email notifications would make for great real time notifications once I take the time to parse those emails and build them into the application somehow. But also it seems valid that declined transactions are &lt;em&gt;not&lt;&#x2F;em&gt; listed in my transaction history on the website. This is area of functionality I hadn&#x27;t anticipated and that I probably couldn&#x27;t build using Selenium.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;all-the-dates-were-wrong&quot;&gt;All the Dates were Wrong&lt;&#x2F;h4&gt;
&lt;p&gt;In banking, there&#x27;s generally two dates associated with any transaction: the posted date and the effective date. The posted date is when the bank first learned about this transaction. The other date is called the &amp;quot;effective&amp;quot; date, and this is when the bank formally &amp;quot;clears&amp;quot; the transaction. If the bank doesn&#x27;t clear the transaction immediately, it could be considered &amp;quot;pending&amp;quot; for up to 2 or 3 business days, and it probably didn&#x27;t affect your balance until it clears.&lt;&#x2F;p&gt;
&lt;p&gt;All that to say, for debit card transactions on checking accounts, I get emails for the posted date so that&#x27;s why the dates in MySQL always a day or two before what my bank shows. This might be a WONTFIX issue, unless I can think up a clever way to solve it later.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;doubled-transactions&quot;&gt;Doubled Transactions&lt;&#x2F;h4&gt;
&lt;p&gt;Related to the last section, sometimes I get a &lt;em&gt;second&lt;&#x2F;em&gt; email that&#x27;s identical to the first email, when the transaction &amp;quot;clears.&amp;quot; But not always.&lt;&#x2F;p&gt;
&lt;p&gt;So, I think I&#x27;ve only just solved this problem in the last week. Before I insert a new row into MySQL, I construct an ID in my script. The ID is a hash of only some columns I&#x27;ll insert:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Transaction date&lt;&#x2F;li&gt;
&lt;li&gt;Description&lt;&#x2F;li&gt;
&lt;li&gt;Amount&lt;&#x2F;li&gt;
&lt;li&gt;Account&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It doesn&#x27;t include the email date nor email id (did you know emails have uids?? I only recently learned that from the IMAP protocol). Now, when there are both posted and effective date emails for the same transaction, I get MySQL errors about duplicate IDs. Success!&lt;&#x2F;p&gt;
&lt;h5 id=&quot;not-quite-success&quot;&gt;Not Quite Success&lt;&#x2F;h5&gt;
&lt;p&gt;So It turns out, in rare cases there really are multiple transactions on the same day with the same description and amount and account. Gah! How do you distinguish between these cases?&lt;&#x2F;p&gt;
&lt;p&gt;You can&#x27;t. Not using transaction emails.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;daily-account-balances&quot;&gt;Daily Account Balances&lt;&#x2F;h3&gt;
&lt;p&gt;However, banks also provide daily account balance emails, so we should at least be able to detect when the actual bank balance isn&#x27;t the sum of the transactions from the previous day.&lt;&#x2F;p&gt;
&lt;p&gt;Also, tracking the balance of an account (even if its only daily) is super valuable as an independent data point for my application.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-close-am-i&quot;&gt;How Close Am I?&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s really hard to say.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been focused on creating a dead simple cli in Rust, and I&#x27;m learning interesting things about the IMAP protocol, accounting, and Rust. My data is quickly becoming more accurate, but it&#x27;s still not right. I&#x27;m also learning how to work on side projects more effectively by breaking down work into multi-hour tasks, and how to prioritize the right next step. It&#x27;s actually fun and I have some momentum. I hangout with friends and (rarely) rage program anymore.&lt;&#x2F;p&gt;
&lt;p&gt;But nothing comes for free. I still mostly sacrifice sleep or time with friends or that just using ledger directly so I can work on this project. Honestly, I haven&#x27;t 100% proven that emails can accurately track my net worth. Once that (hopefully) pans out, there&#x27;s a killer interface for personal finances and setup an LLC and marketing and yada yada to do.&lt;&#x2F;p&gt;
&lt;p&gt;But I&#x27;m still optimistic, and for now I&#x27;m going to keep chugging away because this is one way I choose to try to create meaning for myself (and there&#x27;s still nothing out there that quite does what I want).&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Web Analytics without Google - A Just Tracking Theory</title>
		<published>2019-07-29T00:00:00+00:00</published>
		<updated>2019-07-29T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/theory-of-just-tracking-without-google/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/theory-of-just-tracking-without-google/</id>
		<content type="html">&lt;p&gt;I&#x27;ve been thinking about adding some kind of analytics to this website for quite some time, but I&#x27;ve been feeling very conflicted about it. Here&#x27;s my current thoughts on why tracking might not be evil...at least sometimes!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;just-war-tracking-theory&quot;&gt;Just &lt;s&gt;War&lt;&#x2F;s&gt; Tracking Theory&lt;&#x2F;h2&gt;
&lt;p&gt;Although philosophers and statespersons debate the morality of war, marketing people don&#x27;t ever really consider this in my experience. For philosophers, there are two questions. One, is war &lt;em&gt;ever&lt;&#x2F;em&gt; justifiable? Two, under &lt;em&gt;what conditions&lt;&#x2F;em&gt; is a country justified in going to war? In an age of endless data breaches and tampered elections, I believe a moral theory underlying why it&#x27;s &lt;em&gt;ever&lt;&#x2F;em&gt; morally permissible to do web analytics is necessary. Without such a theory and conditions under which tracking is morally permissible, content creators run the risk that society will demonize our efforts to share our knowledge and talents with the world.&lt;&#x2F;p&gt;
&lt;p&gt;I have two basic arguments for why I believe tracking is moral under certain conditions.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-friendly-store-keeper-argument&quot;&gt;The Friendly-Store-Keeper Argument&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;em&gt;A vaguely human shaped bundle of black clothing waddles into your newstand, and puts a gloved hand on a copy of the local newspaper. After leafing through a few pages, the masked bundle sets down the paper and picks up the sports section of a national paper. Finally, the bundle grunts through a voice-hiding mouth-piece and buys a pack of gum at the counter with exact change. You smile politely, ask if they want a receipt, and finally wave farewell as the bundle waddles away.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;What&#x27;s odd about my imaginary story is that we don&#x27;t expect anywhere near that level of anonymity when conducting business in person offline. But, if there were no web tracking, this is a good analogy for how a website owner would see activity on her site. Just as its normal and not creepy for a shopper keeper to notice my skin color and age when I walk into her store, likewise its reasonable for individual websites to learn &lt;em&gt;something&lt;&#x2F;em&gt; about my visits to their site. For example, I assume a website owner will know what pages I visited, perhaps if I&#x27;m a first time visitor or a returning guest, etc.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-revenue-by-other-means-argument&quot;&gt;The Revenue-By-Other-Means Argument&lt;&#x2F;h3&gt;
&lt;p&gt;I really like the Economist. I pay a few hundred dollars a year to receive one issue per week chocked full of British humor and surprising stories from across the world. Last week&#x27;s top article: Operatic Cross Dressing in China. However, if I didn&#x27;t pay the Economist, when would the reporters find time to do the research for the paper, or take all the tiny little pictures, or reflect on the sad state of the world in 2019? Answer: they wouldn&#x27;t. In fact, my subscription alone doesn&#x27;t even fully compensate all the expenses of the paper, so the Economist (very tastefully) solicits advertising and receives revenue by another means.&lt;&#x2F;p&gt;
&lt;p&gt;Similarly, there are many incredible resources online. If no one pays to use them, there won&#x27;t be as many. Tracking provides information necessary to convince advertisers to subsidize something valuable to society that is hard to finance otherwise.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;consumer-espionage&quot;&gt;Consumer Espionage&lt;&#x2F;h3&gt;
&lt;p&gt;Google Analytics is doing something different. To return to the friendly store keeper analogy, while I think Google should mind their store, I object to their following you down the block, into neighboring stores, and then into your house to stare at your photos. The scope of surveillance from Google is truly staggering -- the microphone in your pocket, the camera in your phone, every email and text message and conference call, your location at every moment and even the questions on your mind as you search for answers.&lt;&#x2F;p&gt;
&lt;p&gt;Building a detailed profile of a person surreptitiously from many sources resembles nothing so much as espionage. While spies secretly collect information, they do it to manipulate, physically harm, or discredit their opponents. Google&#x27;s goal of manipulating one&#x27;s purchasing behavior seems much closer to a sort of &amp;quot;consumer&amp;quot; espionage.&lt;&#x2F;p&gt;
&lt;p&gt;Why should you care? Because if you use Google Analytics on your blog or website, you&#x27;ve become a spy for Google by allowing them into your store.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;be-the-friendly-store-keeper-you-want-to-see-in-the-world&quot;&gt;Be the Friendly Store Keeper You Want To See in the World&lt;&#x2F;h2&gt;
&lt;p&gt;How can an individual site owner ensure their tracking is more like a friendly store keeper, and less like consumer espionage? Just don&#x27;t use Google Analytics, and you&#x27;ll stop feeding the corporate surveillance machine.&lt;&#x2F;p&gt;
&lt;p&gt;It turns out, at least for smaller blogs and websites, this isn&#x27;t all that hard to avoid. There are a &lt;a href=&quot;https:&#x2F;&#x2F;matomo.org&#x2F;&quot;&gt;handful&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;usefathom.com&#x2F;&quot;&gt;of&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;goaccess.io&#x2F;&quot;&gt;alternatives&lt;&#x2F;a&gt; that you can even run yourself, but any data analytics platform which is not Google is a huge win for privacy.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;with-great-data-comes-great-responsibility&quot;&gt;With Great Data, Comes Great Responsibility&lt;&#x2F;h3&gt;
&lt;p&gt;But, in an age of data breaches and tampered elections, it&#x27;s not sufficient to avoid surveillance. We have to be responsible for the data we collect. I think we&#x27;re just starting to think about this as a society, but to me personally it means at least these additional things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Keep &lt;s&gt;your&lt;&#x2F;s&gt; other people&#x27;s data safe&lt;&#x2F;li&gt;
&lt;li&gt;Don&#x27;t collect more data you have to&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;m not a security expert, but I do think that most security issues stem from a lack of concern about security. If everybody cared just a little bit more to follow basic best practices from the likes of &lt;a href=&quot;https:&#x2F;&#x2F;www.owasp.org&#x2F;index.php&#x2F;Category:Attack&quot;&gt;OWASP&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;www.nist.gov&#x2F;itl&#x2F;tig&#x2F;back-basics-multi-factor-authentication&quot;&gt;NIST&lt;&#x2F;a&gt;, everyone&#x27;s data would be a little bit safer.&lt;&#x2F;p&gt;
&lt;p&gt;In terms of minimizing data collection, analytics tools too often dictate this. However, I think its possible to change the status quo and demand more privacy from our analytics tools.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next-time&quot;&gt;Next Time&lt;&#x2F;h2&gt;
&lt;p&gt;This was the first part in a mini-series on tracking without Google. Next up, what metrics are even worth tracking and a review of alternatives to Google Analytics.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Please Don&#x27;t Deply Before You Test</title>
		<published>2019-07-26T00:00:00+00:00</published>
		<updated>2019-07-26T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/please-dont-deploy-before-you-test/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/please-dont-deploy-before-you-test/</id>
		<content type="html">&lt;p&gt;In the small to medium software teams that I&#x27;ve been on, our development cycle is inefficient and error prone &lt;em&gt;at a conceptual level&lt;&#x2F;em&gt;. Code changes are fully tested only after becoming the source of truth for the team, causing rollbacks that corrupt git history. Too often, developers are unable to determine whether a piece of code will actually work in production before they ship their code. I propose several practices to more fully test your code before deploying it to your customers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-the-stage-typical-development-cycles&quot;&gt;Setting the Stage: Typical Development Cycles&lt;&#x2F;h2&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;Laptop -&amp;gt;  CI  -&amp;gt; Approval -&amp;gt; Staging -&amp;gt; Production
 8hrs     30min      8hrs       1hr         Years
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;Length of time branch exists at each phase of development cycle&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In this kind of a workflow, the only production-like environment used to test software happens immediately before sending your code to your customers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-specifically-is-bad-about-staging-right-before-production&quot;&gt;What, Specifically, Is Bad about Staging Right Before Production?&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;problem-1-prod-is-just-different-man&quot;&gt;Problem 1: Prod is just different, man&lt;&#x2F;h3&gt;
&lt;p&gt;There are often significant differences which emerge only in a production-like infrastructure. I once spent days developing and testing delete functionality for DynamoDB, adding in conditions to the delete to ensure atomicity in cases of writes happening concurrently, only to discover that the mocking library for DynamoDB had a different version of conditions than the DynamoDB running in AWS. If I could have easily run this code against real DynamoDB, I would have discovered this issue mucher earlier. While this kind of issue doesn&#x27;t happen to all developers everyday, it&#x27;s not uncommon among my peers and it can double or triple the time required to complete even simple tasks.&lt;&#x2F;p&gt;
&lt;p&gt;These kinds of problems aren&#x27;t limited to proprietary cloud systems either. I&#x27;ve personally encountered foot guns when using open source components, such as Celery, Redis, Django&#x27;s ORM, and MongoDB -- because these are the infrastructure facing technologies I&#x27;ve run lately. The production infrastructure wasn&#x27;t properly mimicked anywhere, except maybe in staging.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;problem-2-dips-on-staging&quot;&gt;Problem 2: Dips on Staging&lt;&#x2F;h3&gt;
&lt;p&gt;Waiting for access to staging is another problem. At one of my jobs, we manually tested each branch after merging. I remember rolling out a big change to add SSL support for a large new customer. This change had the potential to break all images, stylesheets, javascript and basically everything if I didn&#x27;t get it right. So, I took staging from the team for a few hours to test it manually, but blocked the team from deploying. Anyway, I finally got it right on the third try. For nearly every team I know of where there&#x27;s a staging environment, I hear about production deploys being blocked due to the need to deploy various experimental changes before hitting production.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;problem-3-bugs-going-viral&quot;&gt;Problem 3: Bugs Going Viral&lt;&#x2F;h3&gt;
&lt;p&gt;In teams of up to a hundred engineers working in the same codebase, if wrong assumptions about infrastructure reach production, its entirely possible for one developer to pull down the authoritative branch (let&#x27;s call it &amp;quot;master&amp;quot;) during the window of time that this wrong assumption has not yet been rolled back. Defective code like this can go viral and reoccur even after a rollback.&lt;&#x2F;p&gt;
&lt;p&gt;Its actually even worse than that. If automated tests run against a staging environment &lt;em&gt;only&lt;&#x2F;em&gt; after merging to master, its entirely possible that an unwitting developer pulled in the bad code which never reached production. At some point later, she discovers that her branch doesn&#x27;t pass tests for code unrelated to her changes. At one company I recently worked for, this problem became so widespread that the QA team built tools to toggle individual tests via a Google spreadsheet so that they could increase developer velocity. A few times, I&#x27;d spend 30 minutes to an hour diagnosing a bug unrelated to my own code, open up a PR to fix the unrelated code, and discover that another PR or two had already been opened and reviewed (but perhaps not yet merged to master) before mine was opened.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fixing-software-development&quot;&gt;Fixing Software Development&lt;&#x2F;h2&gt;
&lt;p&gt;For nearly all these issues I&#x27;ve identified above, some kind of test already existed but the test ran on staging too late. What if every change could land on stage automatically &lt;em&gt;before&lt;&#x2F;em&gt; merging to master? What if you could manually poke at this stage at your leisure, without any time pressure or blocking other engineers release? Fewer bugs and greater velocity.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;solution-1-make-a-staging-per-branch&quot;&gt;Solution 1. Make a &amp;quot;Staging&amp;quot; Per Branch&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Expense&lt;&#x2F;th&gt;&lt;th&gt;Hourly&lt;&#x2F;th&gt;&lt;th&gt;Annual&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&amp;quot;Web Developer&amp;quot; in Kentucky&lt;&#x2F;td&gt;&lt;td&gt;$36.29&lt;&#x2F;td&gt;&lt;td&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.glassdoor.com&#x2F;Salaries&#x2F;kentucky-web-developer-salary-SRCH_IL.0,8_IS1141_KO9,22.htm&quot;&gt;$75,487&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;m5d.24xlarge on-demand EC2 instance in us-west-1&lt;&#x2F;td&gt;&lt;td&gt;&lt;a href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;ec2&#x2F;pricing&#x2F;on-demand&#x2F;&quot;&gt;$6.38&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td&gt;$13,023&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;ul&gt;
&lt;li&gt;The hourly wage is calculated by dividing the yearly salary into 52 weeks, and then dividing each week into 40 hours&lt;&#x2F;li&gt;
&lt;li&gt;The annual cost of an m5d.24xlarge is calculated for the a 51 week year of 40hrs per week&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Even the cheapest labor is significantly more expensive (more than 5x) than the priciest cloud computing. But, if you hire experienced web developers in the Bay Area (or other coastal cities), the labor costs could be 2 to 3 times that of the our colleagues in Kentucky. And the EC2 instances we&#x27;d be testing on is likely much less expensive (do you actually need 384 GiB of RAM and 96 cores for your production workloads?). Any work an engineer is doing that &lt;em&gt;can&lt;&#x2F;em&gt; be replace by a cloud computing instance should be.&lt;&#x2F;p&gt;
&lt;p&gt;So, if all of our developers are sitting idle, waiting for a chance to test their work on a production-like environment, that looks like an ideal point to arbitrage the compute &#x2F; engineer cost difference. If all of our engineers can spend less time waiting for tests to complete, and more time delivering features or fixing bugs, that means the company could be getting more value out of the workforce they already have without a significant outlay.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;objection-our-production-infrastructure-is-hard-to-replicate&quot;&gt;Objection! Our production infrastructure is hard to replicate&lt;&#x2F;h4&gt;
&lt;p&gt;Congratulations! You just found an argument that gives you budget to do some real devops on your production infrastructure. Bonus: you&#x27;ll get really good at disaster recovery if every branch is mini-rollout of your production infrastructure. The details of how you do this are not something I can unpack in this blog post right now, but there are lots of other great resources on this starting with the &lt;a href=&quot;https:&#x2F;&#x2F;12factor.net&#x2F;&quot;&gt;12 factor app&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;objection-how-could-an-expensive-ec2-instance-possibly-make-any-difference&quot;&gt;Objection! How could an expensive EC2 instance possibly make any difference?&lt;&#x2F;h4&gt;
&lt;p&gt;If you&#x27;re not already in the cloud, it&#x27;s the same principle: you&#x27;d want to buy a set of hardware which is &lt;em&gt;identical&lt;&#x2F;em&gt; to your production infrastructure and configured using the same set of automation for your on-premises servers. Oh, you don&#x27;t automation for your on-premises hardware? See previous paragraph.&lt;&#x2F;p&gt;
&lt;p&gt;Either way, the closer your development environment (and particularly your CI environment) is to production, the more effective the tests you already have will be. By deploying and running ALL tests against a production like environment, you will not bogged down with hardware or instance-type specific quirks. As we discussed earlier, if staging is the only production-like environment, and all tests need to pass through staging, you&#x27;ve got a single point of failure and a potentially limiting factor in your CI&#x2F;CD pipeline. To solve both problems, eliminate the notion of a permanente environment called &amp;quot;staging&amp;quot; and create ephemeral environments for each branch that&#x27;s developed.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;objection-this-still-seems-really-expensive&quot;&gt;Objection! This still seems really expensive&lt;&#x2F;h4&gt;
&lt;p&gt;There&#x27;s a lot you can do to cut costs on this operation. Here&#x27;s a few ideas:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Invest in autoscaling so that when your development environments are idle, they scale to 0&lt;&#x2F;li&gt;
&lt;li&gt;When the branch is deleted, automation should be able to clean up that ephemeral branch to reduce costs&lt;&#x2F;li&gt;
&lt;li&gt;If the whole company works in a few adjacent timezones, turn off all the machines for the night via automation. Bonus: employees will avoid burnout by going home at a &amp;quot;reasonable&amp;quot; hour&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These techniques, alone or in combination, should keep your costs minimal while providing strong guarrantees about the code you ship to production.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;objection-what-about-databases-credentials-to-vendor-integrations-etc&quot;&gt;Objection! What about databases, credentials to vendor integrations, etc?&lt;&#x2F;h4&gt;
&lt;p&gt;This part depends heavily on your context, so I can&#x27;t provide the right answers for your case. However there &lt;em&gt;are&lt;&#x2F;em&gt; strategies you can try.&lt;&#x2F;p&gt;
&lt;p&gt;For relational databases, you can spin up a database cluster that&#x27;s an exact mirror of production, but only used by development environments. Each of your branches can create a separately named set of databases for it and your automation can run migrations or pull a recent snapshot from production (probably best to sterilize any sensitive data in there first!). For DynamoDB or other similar proprietary NoSQL databases, you can just create a duplicate AWS&#x2F;GCP&#x2F;Azure account, and namespace your database.&lt;&#x2F;p&gt;
&lt;p&gt;For other third party integrations, I&#x27;d recommend making a separate account and reusing credentials where possible. Speak to your vendor to find out how other customers are solving this problem -- if the vendor is smart, they&#x27;ve find a way to solve your issues.&lt;&#x2F;p&gt;
&lt;p&gt;None of these techniques are new or particularly clever. They just take an investment of time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;solution-2-no-more-sneaky-3-ways&quot;&gt;Solution 2. No More Sneaky 3-ways&lt;&#x2F;h3&gt;
&lt;p&gt;When your Github repository allows merge commits (a &amp;quot;three-way merge&amp;quot; technically), this means that its very possible code from your branch won&#x27;t work after its merged into master. There&#x27;s very little guarranteed about the logic or syntax of your code after a 3 way merge. However, if its merged to master, your bugs are going to go viral -- even when the tests are already written that would catch these bugs!&lt;&#x2F;p&gt;
&lt;h4 id=&quot;rebasing-is-the-answer&quot;&gt;Rebasing Is the Answer!&lt;&#x2F;h4&gt;
&lt;p&gt;However, if your branch must be rebased against master and is only allowed to be a fast-forward merge, then you have guarranteed that the code in your PR will be &lt;em&gt;exactly&lt;&#x2F;em&gt; the same code in master. This means the test result will be the same for anything that relies purely on the code. This is huge! The key point here is not how to use git correctly, but how to guarrantee that a good starting point for other developers to base their own work upon.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;rebasing-isn-t-really-the-answer&quot;&gt;Rebasing Isn&#x27;t Really The Answer&lt;&#x2F;h4&gt;
&lt;p&gt;In fact, at a certain scale, simply doing rebases probably won&#x27;t work anymore. The problem will become that developers can&#x27;t rebase and finish tests as fast as code is being deployed to production and merged to master. This leads to a vicious cycle of rebasing endlessly against the current &lt;code&gt;HEAD&lt;&#x2F;code&gt; of master, like racing up an escalator going down.&lt;&#x2F;p&gt;
&lt;p&gt;OpenStack addresses this problem by testing combinations of branches together all the time to find combinations that will work &lt;em&gt;before&lt;&#x2F;em&gt; they are merged to master. They&#x27;ve custom built a tool called &lt;a href=&quot;https:&#x2F;&#x2F;zuul-ci.org&#x2F;&quot;&gt;zuul&lt;&#x2F;a&gt; for this.&lt;&#x2F;p&gt;
&lt;p&gt;From chatting with friends at Facebook, I &lt;em&gt;think&lt;&#x2F;em&gt; they do something like queue a series of changes, and put these changes into a deterministic order. By ordering the change requests, they&#x27;ve effectively gotten the same guarrantees as a rebase-based workflow.&lt;&#x2F;p&gt;
&lt;p&gt;Netflix seems to tackle this problem by breaking things into microservices so that there aren&#x27;t huge codebases collaborated on by thousands of engineers together.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;but-seriously-rebasing-kinda-is-the-answer&quot;&gt;But, Seriously, Rebasing Kinda Is the Answer&lt;&#x2F;h4&gt;
&lt;p&gt;So, this idea is very powerful and much deeper than just &amp;quot;do rebases&amp;quot;. The real idea here is &amp;quot;test before you merge.&amp;quot; It just so happens that rebasing, with few enough engineers sharing a repository, tends to be an easy way to achieve this. Regardless of how you eliminate three way merges, these kind of tactics make your earlier branch tests more effective, making your existing tests work earlier.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;solution-3-test-outside-the-box&quot;&gt;Solution 3. Test Outside The Box&lt;&#x2F;h3&gt;
&lt;p&gt;Unit and integration tests that I&#x27;ve written and seen typically work inside the same process as the application code under test. Mocking is used to speed up expensive operations and create very specific situations to constrain tests. All of that is great at testing very specific code paths efficiently, and it gets us something like 80% of the correctness guarrantees with 20% of the work of, say, fuzzing. So, this is really great stuff and its awesome we do it.&lt;&#x2F;p&gt;
&lt;p&gt;But, it doesn&#x27;t really answer if the application actually works for users. I like to get paid. I like it when users like the software I write and they pay for it...you know, because it worked for them. Life is hard enough without glitchy software, why make people suffer more?&lt;&#x2F;p&gt;
&lt;p&gt;By buttressing our &amp;quot;whitebox&amp;quot; testing with tests that operate outside the application as a separate process, we can check detect serious regressions. Additionally, these tests are &lt;em&gt;very&lt;&#x2F;em&gt; portable. With a tinsy bit of cleverness, you can write blackbox tests to run against a remote machine just as easier as a separate process on your same machine.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;cons-with-blackbox-testing&quot;&gt;Cons with Blackbox Testing&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;strong&gt;Upfront investment&lt;&#x2F;strong&gt; in blackbox testing can discourage managers. However, I find that writing blackbox tests during a proof-of-concept phase to be worthwhile effort. Because the tests are portable and separate from the application, I can throw away my prototype and keep the same tests. If I&#x27;ve written enough coverage of my application using blackbox testing, I can be sure that any services using my application can switch over to the new application flawlessly. I can even write the application in a different language my blackbox test!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Maintenance&lt;&#x2F;strong&gt; of blackbox tests can intimidating. My prefered metholodogy would be make the blackbox tests mandatory for CI, and encourage developers to add blackbox tests alongside new features. This does impact development velocity, but in many cases the existing tests can remain unchanged.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;pros-with-blackbox-testing&quot;&gt;Pros with BlackBox Testing&lt;&#x2F;h4&gt;
&lt;p&gt;You know those annoying bits of code to test near your program entrance? Using &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;brycefisher&#x2F;coveragepy-flask&#x2F;&quot;&gt;blackbox testing with code coverage&lt;&#x2F;a&gt; tools can make it easy to see that main method and many other wide swathes of your application have abundant test coverage.&lt;&#x2F;p&gt;
&lt;p&gt;Again, because blackbox tests can be written for portability, you can run them against production like environments from your own machine or from CI and experiment with changes before you get to production...if you only had your own production like environment...&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Each of my three solutions can work separately. But, there&#x27;s magic that happens if you put them together: if code can only be fast-forward merged, and is tested on your development branch from a staging environment, there&#x27;s no need to run the test suite again on master...until after you deploy to production! In my experience, this could easily 30% of your deployment time that was previously dedicated to re-running tests.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Automate Lets Encrypt SSL Certificate Management with Gitlab Pages</title>
		<published>2019-07-21T00:00:00+00:00</published>
		<updated>2019-07-21T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/automate-letsencrypt-for-gitlab-pages/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/automate-letsencrypt-for-gitlab-pages/</id>
		<content type="html">&lt;p&gt;Let&#x27;s Encrypt offers free automated SSL certificate issuance, and its become the prefered way to provision and renew SSL certificates nearly overnight since its debut a few years back. Gitlab Pages (where I&#x27;m hosting this blog) encourages users to rely on Let&#x27;s Encrypt but doesn&#x27;t document an automated way to manage this.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-automate-certificate-renewal&quot;&gt;Why Automate Certificate Renewal?&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s Encrypt certificates are only valid for 90 days, which is just often enough to require some effort but too infrequent to ever remember how to do this. Also, the stakes are kinda high: if I don&#x27;t &lt;em&gt;keep&lt;&#x2F;em&gt; SSL working, Google will demote my site in search rankings and visitors who bookmarked and&#x2F;or typed in my URL directly will see something horrible like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2019&#x2F;https-is-broken-around-here.png&quot; alt=&quot;Scary warning box plasters the screen&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-can-we-automate-renewal-of-ssl-certificates-for-gitlab-and-let-s-encrypt&quot;&gt;How Can We Automate Renewal of SSL Certificates for Gitlab and Let&#x27;s Encrypt?&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;Isolate &lt;code&gt;_acme.&amp;lt;YOUR HOST NAME&amp;gt;&lt;&#x2F;code&gt; in a hosted zone record set with Route53&lt;&#x2F;li&gt;
&lt;li&gt;Provision an IAM user with limited permissions to control this hosted zone&#x27;s record sets.&lt;&#x2F;li&gt;
&lt;li&gt;Figure out the certbot-dns-route53 and Gitlab Pages API command to obtain a certificate&lt;&#x2F;li&gt;
&lt;li&gt;Schedule a recurring Gitlab CI to renew regularly&lt;&#x2F;li&gt;
&lt;li&gt;Monitor using Keybase.io and&#x2F;or uptimerobot.com to discover if this stops working.&lt;&#x2F;li&gt;
&lt;li&gt;Profit!&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The main reason for (1) is to separate privileges so that if a hacker obtained the credentials for Route53, they couldn&#x27;t pwn &lt;em&gt;ALL&lt;&#x2F;em&gt; your DNS; they could only replace your SSL certificate for the one domain. This is discussed at length elsewhere. AWS is one of the few DNS providers that provide granular access control for DNS. In 2019.&lt;&#x2F;p&gt;
&lt;p&gt;The secondary reason is so that we can leverage &lt;a href=&quot;https:&#x2F;&#x2F;certbot.eff.org&#x2F;docs&#x2F;using.html#dns-plugins&quot;&gt;certbot&lt;&#x2F;a&gt; with the &lt;a href=&quot;https:&#x2F;&#x2F;certbot-dns-route53.readthedocs.io&#x2F;en&#x2F;stable&#x2F;&quot;&gt;certbot-dns-route53&lt;&#x2F;a&gt;. Previously, I&#x27;ve always used HTTP01 challenges, which require putting a random string LetsEncrypt at a specific path on my site, which worked well when I was directly managing an S3 bucket. However for Gitlab Pages, I can only puth content at a path by pushing commits to Gitlab, but I really don&#x27;t want to do that to my git history. The DNS01 challenge offers an alternative which is orthogonal to git history.&lt;&#x2F;p&gt;
&lt;p&gt;(2) Tests our ability to use all these things together and check the credentials for everything.&lt;&#x2F;p&gt;
&lt;p&gt;(3) Once we&#x27;ve shown that the script works, we just schedule to run automatically.&lt;&#x2F;p&gt;
&lt;p&gt;(4) The whole point of this setup is to avoid doing a lot of manual work. If you have to visit your website every N days and read through the very confusing certbot documentation, you&#x27;ll forget and then something will happen and you won&#x27;t know you&#x27;re down.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dns-setup-with-route53&quot;&gt;DNS Setup with Route53&lt;&#x2F;h2&gt;
&lt;p&gt;Create your Route53 hosted zone with the domain name &lt;strong&gt;&lt;code&gt;_acme-challenge.&amp;lt;DOMAIN NAME FOR SSL&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;. So for this blog, the domain for Route53 hosted zone is &lt;code&gt;_acme-challenge.bryce.fisher-fleig.org&lt;&#x2F;code&gt;. Look for the alphanumeric hostzone ID in the AWS console and remember this for the next step.&lt;&#x2F;p&gt;
&lt;p&gt;If like me, you don&#x27;t use Route53 to manage all your DNS, you&#x27;ll need to make sure that some authoritative records point to the Route53 hosted zone. In my case, I purchased &lt;code&gt;fisher-fleig.org&lt;&#x2F;code&gt; from GoDaddy before Route53 was even an AWS product. So, I had to add records pointing to &lt;code&gt;_acme-challenge.bryce.fisher-fleig.org&lt;&#x2F;code&gt;. I choose to use a &amp;quot;name server&amp;quot; &lt;code&gt;NS&lt;&#x2F;code&gt; record, but you could easily use a &lt;code&gt;CNAME&lt;&#x2F;code&gt; record as well. Let&#x27;s verify that this works by using &lt;code&gt;dig&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ dig -t NS _acme-challenge.bryce.fisher-fleig.org

;; ANSWER SECTION:
_acme-challenge.bryce.fisher-fleig.org.	588 IN NS ns-1800.awsdns-33.co.uk.
_acme-challenge.bryce.fisher-fleig.org.	588 IN NS ns-24.awsdns-03.com.
_acme-challenge.bryce.fisher-fleig.org.	588 IN NS ns-834.awsdns-40.net.
_acme-challenge.bryce.fisher-fleig.org.	588 IN NS ns-1495.awsdns-58.org.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I left out a lot of the dig output not revelant to this, but the &amp;quot;ANSWER SECTION&amp;quot; shows that several domains like &lt;code&gt;*.awsdns-*&lt;&#x2F;code&gt; are acting as name servers for &lt;code&gt;_acme-challenge.bryce.fisher-fleig.org&lt;&#x2F;code&gt;. When you provision a hosted zone in AWS, you&#x27;ll be provided your own list of name servers that are likely different from mine. You&#x27;ll need to consult with your DNS provider&#x27;s documentation for how to setup the NS records for the AWS name servers specific to your hosted zone. Using my name servers likely won&#x27;t work for your domain.&lt;&#x2F;p&gt;
&lt;p&gt;Regardless of where you host your DNS for the domain you want to secure, you should be able to add a TXT record (for example with &amp;quot;hi mom&amp;quot;) inside Route53 for &lt;code&gt;_acme-challenge.&amp;lt;YOUR DOMAIN&amp;gt;&lt;&#x2F;code&gt; and then see that same way in dig a little later:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;dig -t TXT _acme-challenge.&amp;lt;YOUR DOMAIN&amp;gt;

;; ANSWER SECTION
_acme-challenge.&amp;lt;YOUR DOMAIN&amp;gt;. 600 IN  TXT &amp;quot;hi mom&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you can see &amp;quot;hi mom&amp;quot; or whatever value you set, then it works! Move on to the next step.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;iam-setup-to-manage-route53&quot;&gt;IAM Setup to Manage Route53&lt;&#x2F;h2&gt;
&lt;p&gt;Now, create an AWS IAM user with this inline IAM policy:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
    &amp;quot;Version&amp;quot;: &amp;quot;2012-10-17&amp;quot;,
    &amp;quot;Id&amp;quot;: &amp;quot;certbot-dns-route53-bryce-fisher-fleig-org&amp;quot;,
    &amp;quot;Statement&amp;quot;: [
        {
            &amp;quot;Effect&amp;quot;: &amp;quot;Allow&amp;quot;,
            &amp;quot;Action&amp;quot;: [
                &amp;quot;route53:ListHostedZones&amp;quot;,
                &amp;quot;route53:GetChange&amp;quot;
            ],
            &amp;quot;Resource&amp;quot;: [
                &amp;quot;*&amp;quot;
            ]
        },
        {
            &amp;quot;Effect&amp;quot; : &amp;quot;Allow&amp;quot;,
            &amp;quot;Action&amp;quot; : [
                &amp;quot;route53:ChangeResourceRecordSets&amp;quot;
            ],
            &amp;quot;Resource&amp;quot; : [
                &amp;quot;arn:aws:route53:::hostedzone&#x2F;&amp;lt;HOSTED ZONE ID&amp;gt;&amp;quot;
            ]
        }
    ]
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Name the policy something and add it to your user. Store the &lt;code&gt;AWS_ACCESS_KEY_ID&lt;&#x2F;code&gt; and &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;&#x2F;code&gt; for later.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;figure-out-certbot-and-gitlab-pages-api&quot;&gt;Figure out Certbot and Gitlab Pages API&lt;&#x2F;h2&gt;
&lt;p&gt;I spent roughly an hour trying to get the official &lt;a href=&quot;https:&#x2F;&#x2F;pypi.org&#x2F;project&#x2F;certbot-dns-route53&#x2F;&quot;&gt;Pypi certbot-dns-route53 package&lt;&#x2F;a&gt; to work for me locally in a virtualenv on MacOS, and didn&#x27;t have it working. I found the official &lt;a href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;r&#x2F;certbot&#x2F;dns-route53&quot;&gt;Docker image&lt;&#x2F;a&gt; to be much easier to setup and use. Plus, this image can be used with very few tweaks from Gitlab CI.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ask-for-a-certificate&quot;&gt;Ask for a Certificate&lt;&#x2F;h3&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;export AWS_ACCESS_KEY_ID=$ACCESS
export AWS_SECRET_ACCESS_KEY=$SECRET

certbot certonly \
  -n --agree-tos \
  --email &amp;quot;$EMAIL&amp;quot; \
  --dns-route53 \
  -d &amp;quot;$DOMAIN&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;The plugin requires AWS credentials to be passed as environment variables&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;certonly&lt;&#x2F;code&gt; means that certbot won&#x27;t try to install the certificate anyway, it will just output the certificate to a file&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;$EMAIL&lt;&#x2F;code&gt; is your email address (required, but also for notices about certificate expiration)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;$DOMAIN&lt;&#x2F;code&gt; is the domain you want a certificate for&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If that all works, you should now have the secret key in &lt;code&gt;&#x2F;etc&#x2F;letsencrypt&#x2F;live&#x2F;${DOMAIN}&#x2F;privkey.pem&lt;&#x2F;code&gt; and the full certificate chain in &lt;code&gt;&#x2F;etc&#x2F;letsencrypt&#x2F;live&#x2F;${DOMAIN}&#x2F;fullchain.pem&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;obtain-an-api-token-for-gitlab&quot;&gt;Obtain an API Token for Gitlab&lt;&#x2F;h3&gt;
&lt;p&gt;Go to &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;profile&#x2F;personal_access_tokens&quot;&gt;https:&#x2F;&#x2F;gitlab.com&#x2F;profile&#x2F;personal_access_tokens&lt;&#x2F;a&gt; and check the box for Scopes labeled &amp;quot;api&amp;quot;. This token &lt;em&gt;does&lt;&#x2F;em&gt; seem more permissive than perhaps it should. If you want to lock down this down more, you could create a dedicated gitlab user for only your blog instead of all your projects.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;send-the-certificate-to-gitlab-pages&quot;&gt;Send the certificate to Gitlab Pages&lt;&#x2F;h3&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;curl -XPUT \
  -H &amp;quot;PRIVATE-TOKEN: $GITLAB_TOKEN&amp;quot; \
  --form &amp;quot;certificate=$(cat &amp;quot;&#x2F;etc&#x2F;letsencrypt&#x2F;live&#x2F;${DOMAIN}&#x2F;fullchain.pem&amp;quot;)&amp;quot; \
  --form &amp;quot;key=$(cat &amp;quot;&#x2F;etc&#x2F;letsencrypt&#x2F;live&#x2F;${DOMAIN}&#x2F;privkey.pem&amp;quot;)&amp;quot; \
  &amp;quot;https:&#x2F;&#x2F;gitlab.com&#x2F;api&#x2F;v4&#x2F;projects&#x2F;$CI_PROJECT_ID&#x2F;pages&#x2F;domains&#x2F;${DOMAIN}&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;$CI_PROJECT_ID&lt;&#x2F;code&gt; is the unique number Gitlab assigns to each project. For example, this blog has a project ID of 2085659 and you can find it on the main Gitlap project page, just under the title.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2019&#x2F;project-id.png&quot; alt=&quot;Gitlab project ID appears after the words Project ID&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;automate-all-the-things&quot;&gt;Automate All The Things!&lt;&#x2F;h2&gt;
&lt;p&gt;You should be feeling pretty good right now -- you&#x27;ve written up a script that hands a shiny new SSL certificate any time you want. Nice! Now, lets make it happen automatically by setting a scheduled job in Gitlab CI.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setup-the-gitlabci-yaml&quot;&gt;Setup the GitlabCI Yaml&lt;&#x2F;h3&gt;
&lt;p&gt;I spent most of my time figuring out the right incantion for my &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;bff&#x2F;bff.gitlab.io&#x2F;commit&#x2F;15dfd9689365413712130c6f7f0999868cd5bc86?view=inline&quot;&gt;.gitlab-ci.yml&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;letsencrypt:
  only:
    - schedules
  image:
    name: certbot&#x2F;dns-route53
    entrypoint: [&amp;quot;&amp;quot;]
  script:
    - apk add curl
    - &amp;#39;certbot certonly -n --agree-tos --email &amp;quot;${EMAIL}&amp;quot; --dns-route53 -d &amp;quot;${DOMAIN}&amp;quot;&amp;#39;
    - &amp;#39;curl -XPUT -H &amp;quot;PRIVATE-TOKEN: $GITLAB_TOKEN&amp;quot;
       --form &amp;quot;certificate=$(cat &amp;quot;&#x2F;etc&#x2F;letsencrypt&#x2F;live&#x2F;${DOMAIN}&#x2F;fullchain.pem&amp;quot;)&amp;quot;
       --form &amp;quot;key=$(cat &amp;quot;&#x2F;etc&#x2F;letsencrypt&#x2F;live&#x2F;${DOMAIN}&#x2F;privkey.pem&amp;quot;)&amp;quot;
       &amp;quot;https:&#x2F;&#x2F;gitlab.com&#x2F;api&#x2F;v4&#x2F;projects&#x2F;${CI_PROJECT_ID}&#x2F;pages&#x2F;domains&#x2F;${DOMAIN}&amp;quot;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;entrypoint: [&amp;quot;&amp;quot;]&lt;&#x2F;code&gt; -- this was &lt;em&gt;very&lt;&#x2F;em&gt; difficult to figure out, even with &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;ci&#x2F;docker&#x2F;using_docker_images.html#overriding-the-entrypoint-of-an-image&quot;&gt;the documentation&lt;&#x2F;a&gt; in hand. My guess would be that Gitlab Runners actually use &lt;code&gt;docker exec&lt;&#x2F;code&gt; to run &lt;code&gt;script&lt;&#x2F;code&gt; commands on a container, but many containers (like &lt;code&gt;certbot&#x2F;dns-route53&lt;&#x2F;code&gt;) will exit immediately if not given a sensible command for &lt;code&gt;docker run&lt;&#x2F;code&gt;, though to be honest I&#x27;m not quite sure how this works.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;only: schedules&lt;&#x2F;code&gt; -- this is important to prevent every push to your repo from generating a new SSL certificate, but it means you&#x27;ll have to visit the schedules page and click the play button to actually your code&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;apk add curl&lt;&#x2F;code&gt; -- the docker image doesn&#x27;t come with curl pre-installed so we leverage Alpine Linux&#x27;s package manager &lt;code&gt;apk&lt;&#x2F;code&gt; to install it&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;put-it-on-your-schedule&quot;&gt;Put It on Your Schedule&lt;&#x2F;h3&gt;
&lt;p&gt;Gitlab CI schedules are controlled separately from &lt;code&gt;.gitlab-ci.yml&lt;&#x2F;code&gt; via the API and using the website:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2019&#x2F;gitlab-schedules.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I do wish that schedules could be created via the regular &lt;code&gt;.gitlab-ci.yml&lt;&#x2F;code&gt; file used for everything else in Gitlab CI...but at least there&#x27;s an API and a fairly straightforward UI for this. The upshot of separating out the scheduled jobs is that credentials for scheduled jobs can be managed separately from git-push jobs. We can benefit from that by only adding the &lt;code&gt;AWS_ACCESS_KEY_ID&lt;&#x2F;code&gt; into the scheduled job for Lets Encrypt (and not in the regular variables used by git push).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2019&#x2F;new-gitlab-schedule.png&quot; alt=&quot;New Schedule Form&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Pick the right branch here (I used master once I&#x27;d finished manually verifying everything works). These variables will need to be setup for the schedule:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EMAIL&lt;&#x2F;code&gt; - your email address&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;DOMAIN&lt;&#x2F;code&gt; - the domain you want to secure with SSL&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;AWS_ACCESS_KEY_ID&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;&#x2F;code&gt; - you should have this stored away from step 1 above&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;GITLAB_TOKEN&lt;&#x2F;code&gt; - this token you created inside Gitlab in the previous step&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Once its all added, test it out by click &amp;quot;Play&amp;quot; on the schedules overview page and watch the job logs to look for problems.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;monitor-on-the-cheap&quot;&gt;Monitor on the Cheap&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve used &lt;a href=&quot;https:&#x2F;&#x2F;uptimerobot.com&#x2F;&quot;&gt;UptimeRobot&lt;&#x2F;a&gt; for several years and I&#x27;m pretty happy with their free offering for personal projects.&lt;&#x2F;p&gt;
&lt;p&gt;However, I&#x27;ve found that &lt;a href=&quot;https:&#x2F;&#x2F;keybase.io&#x2F;&quot;&gt;Keybase&lt;&#x2F;a&gt; (which is for encrypted chat and file sharing, not monitoring things) is actually &lt;em&gt;really&lt;&#x2F;em&gt; good at noticing when my SSL certificate is expired on my blog...and its also free.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not going to go into how to use these products since you&#x27;ve suffered through enough of my yammerings already, but I think either one can be a great option.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;profit&quot;&gt;Profit!&lt;&#x2F;h2&gt;
&lt;p&gt;Using this system, you should have a perpetually valid SSL certificate, replaced at the frequency of your choosing, and costing you a few cents per month on AWS Route53 bills.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Dangers of CloudFront for Small Sites</title>
		<published>2019-07-05T00:00:00+00:00</published>
		<updated>2019-07-05T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/dangers-of-cloudfront-for-small-sites/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/dangers-of-cloudfront-for-small-sites/</id>
		<content type="html">&lt;p&gt;For many years, I&#x27;ve relied on CloudFront to provide a professional level of availability for pennies a month. However, for the last two months, I had to take this blog offline to protect my wallet.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;history-of-my-blog-s-tech-stack&quot;&gt;History of my Blog&#x27;s Tech Stack&lt;&#x2F;h2&gt;
&lt;p&gt;When I first started blogging about 6 years ago, I was a passionate Drupal developer, and I built my content on a simple Drupal site.&lt;&#x2F;p&gt;
&lt;p&gt;Fast forward to 2014, I learned about AWS S3 and did a fair bit of work using CloudFront for some Drupal sites I running at work. Once I learned about Jekyll, I rebuilt my blog using Jekyll and used the aws cli to push that content to &lt;a href=&quot;&#x2F;blog&#x2F;setting-up-ssl-on-aws-cloudfront-and-s3&#x2F;index.html&quot;&gt;S3 where CloudFront served the content over HTTPS with a custom SSL certificate&lt;&#x2F;a&gt;. Running a production website without managing servers is what made me fall in love with cloud computing.&lt;&#x2F;p&gt;
&lt;p&gt;Eventually, I figured out how to use Travis CI to automate pushing new content to S3, migrated to Gitlab&#x2F;CI, and added the ability to have each branch of my blog create a new S3 bucket for me to preview the content. I added simple bash scripts to garbage collect old buckets, and do all of the tasks I felt needed it.&lt;&#x2F;p&gt;
&lt;p&gt;Then, last week, I gave up all of the power and control I had over this blog from managing CloudFront myself and ruefully switched to Gitlab Pages.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-happened-to-my-blog-on-cloudfront&quot;&gt;What Happened To My Blog on CloudFront&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2019&#x2F;aws-spend-july-march.png&quot; alt=&quot;Monthly spending from July 2018 until March 2019 for this blog ranging from $0.20 to $1.80&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Yes, the scale on this graph is from $0 to $2 per month&lt;&#x2F;em&gt;. Up until March of 2019, I&#x27;d never spent more than $2 on my whole AWS account in a single month.&lt;&#x2F;p&gt;
&lt;p&gt;Then, a funny happened in April:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2019&#x2F;aws-spend-july-april.png&quot; alt=&quot;Monthly spending from July 2018 until April 2019 for this blog -- April is $164 whereas no previous month was more than $2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When I got this bill in May, I immediately turned off the CloudFront distribution and slowly investigated what went wrong.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-did-these-costs-come-from&quot;&gt;Where did these costs come from?&lt;&#x2F;h2&gt;
&lt;p&gt;I discovered IP addresses based in Turkey were making HTTP HEAD requests for &lt;code&gt;&#x2F;ping.txt&lt;&#x2F;code&gt; (which has never existed). I&#x27;ve lost the report from CloudFront (since the reports have a  3 month retention period), but the Cost Explorer and pricing information shows:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;$0.012 per 10,000 requests&lt;&#x2F;li&gt;
&lt;li&gt;$150 total costs&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So, $150 * 10,000 requests &#x2F; $0.012 = &lt;em&gt;125 million requests&lt;&#x2F;em&gt;, or roughly 1 request per second. I thought that there were more like 15 billion requests, but the math does not check out for that. If my memory is correct, then CloudFront serviced close to 5000 requests per second for a month.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-didn-t-i-notice-this-earlier&quot;&gt;Why Didn&#x27;t I Notice This Earlier?&lt;&#x2F;h2&gt;
&lt;p&gt;I didn&#x27;t setup billing alerts with AWS. &lt;strong&gt;If you&#x27;re running personal infrastructure on AWS and don&#x27;t have billing alerts, stop what you&#x27;re doing and go setup some billing alerts &lt;em&gt;right now&lt;&#x2F;em&gt;.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The upshot of all this is that CloudFront kept my blog online and available during a spike in traffic of many orders of magnitude. I didn&#x27;t have any downtime detected by my monitoring tools.&lt;&#x2F;p&gt;
&lt;p&gt;But the downside of this uptime is that I literally paid with my wallet. I&#x27;d gladly have traded some downtime (like weeks of downtime) to have that $150 back in my pocket.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-can-i-protect-my-blog&quot;&gt;How Can I Protect My Blog?&lt;&#x2F;h2&gt;
&lt;p&gt;This is the really frustrating part for me. There really weren&#x27;t any great options that I could find. Here&#x27;s what looked into:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;geo-blocking-inside-cloudfront&quot;&gt;Geo-blocking inside CloudFront&lt;&#x2F;h3&gt;
&lt;p&gt;CloudFront can be configured to block access to content based on the presumed physical location of visitor&#x27;s IP address. The problem is you still pay for the geo-blocked requests. The point of geo-blocking is to (a) protect your original and (b) limit access to your content. Geo-blocking isn&#x27;t designed to really save you from an attacker.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;waf-and-ddos-protection&quot;&gt;WAF and DDOS Protection&lt;&#x2F;h3&gt;
&lt;p&gt;Like CloudFront geo-blocking, all that Web Application Firewall (WAF) and the various DDoS protection products try to do is restrict access to the origin, not prevent CDN costs.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dns-to-the-rescue&quot;&gt;DNS to the Rescue!&lt;&#x2F;h3&gt;
&lt;p&gt;So, what we learned above is basically if a request goes to CloudFront, you must pay for that request. Full stop. But, what if we could selectively control which requests went to a CloudFront? What if we could use DNS to deny access to CloudFront to the attacker?&lt;&#x2F;p&gt;
&lt;p&gt;At work, we use a product called Cedexis which is just all kinds of amazing. Basically, its a programmable DNS resolver. You define several &amp;quot;platforms&amp;quot; which are CNAMEs that Cedexis could resolve, and then you feed analytics into Cedexis, and at DNS resolution, custom javascript applications have access to the analytics data to let pick different platforms based on which one is most available or performant or cheap. Unfortunately, Cedexis is only really marketed as an enterprise product, not a hobby developer product.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, AWS Route53 has DNS policies that can do basic geo-blocking (and probably a lot more too). Sadly, each policy costs $50&#x2F;month, so that doesn&#x27;t really help me keep costs down to under $2.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ditch-cdns-for-vms&quot;&gt;Ditch CDNs for VMs&lt;&#x2F;h3&gt;
&lt;p&gt;This is actually a pretty reasonable idea. Getting a fixed $5&#x2F;month cost and variable uptime is exactly the set of tradeoffs I&#x27;m looking for. Also, it gives me considerable flexibility to build out automation for previewing different branches of my git repository for this blog.&lt;&#x2F;p&gt;
&lt;p&gt;The downsides are that I&#x27;d only be willing to pay for one VM running in a single location, so pagespeed would likely vary based on the visitor&#x27;s distance from my VM, and I do get some percentage of my visitors from the UK and India. Also, I&#x27;d be committing to spending $5&#x2F;month regardless of whether the server was idle or active during that time. Also, now instead of sitting back and letting CloudFront provision and monitor individual servers, I&#x27;d have to setup monitoring and alerting for this VM.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;diy-cedexis&quot;&gt;DIY Cedexis&lt;&#x2F;h3&gt;
&lt;p&gt;I actually want to do this. After looking at BIND and CoreDNS, I didn&#x27;t see anything quite as powerful as what Cedexis OpenMix provides, but I did find &lt;a href=&quot;https:&#x2F;&#x2F;jameshfisher.com&#x2F;2017&#x2F;08&#x2F;04&#x2F;golang-dns-server&#x2F;&quot;&gt;this golang library for DNS&lt;&#x2F;a&gt; that&#x27;s a promising start.&lt;&#x2F;p&gt;
&lt;p&gt;My main concerns here are about downtime and abuse of the DNS resolver...but I plan to experiment in this area. If I get this right, I could potentially keep using CloudFront + S3 + LetsEncrypt to manage this website. The endgame for this would be to automate attack detection and mitigation as a sort of fail2ban at DNS layer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gitlab-pages&quot;&gt;Gitlab Pages&lt;&#x2F;h2&gt;
&lt;p&gt;I wound up going with Gitlab Pages for now because my time is so limited and I wanted to be back online (after weeks of downtime) as soon as possible.&lt;&#x2F;p&gt;
&lt;p&gt;The biggest things I lose here are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;easy way to renew LetsEncrypt certificates&lt;&#x2F;li&gt;
&lt;li&gt;branch previews of my markdown&lt;&#x2F;li&gt;
&lt;li&gt;visibility into whats going on with the delivery of my blog&lt;&#x2F;li&gt;
&lt;li&gt;control over custom redirects, protocols, etc&lt;&#x2F;li&gt;
&lt;li&gt;surprise bills for $150&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;parting-thoughts&quot;&gt;Parting Thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;If you know of other ways to financially mitigate even simple attacks while keeping CloudFront in my stack under my control, please do tell me! Also, billing alerts are key.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Separate IO from Logic</title>
		<published>2018-05-17T00:00:00+00:00</published>
		<updated>2018-05-17T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/separate-io-from-logic/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/separate-io-from-logic/</id>
		<content type="html">&lt;p&gt;I&#x27;ve come across some terrible code at work lately. It was written by me a few months earlier. When I tried to think about why it was so bad, I realized that I hadn&#x27;t separated my business logic from all the &amp;quot;glue code&amp;quot; to other network services. Once I recognized this anti-pattern, I started seeing it lots of places.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-this-terrible-code&quot;&gt;What Is This Terrible Code?&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s something very similar to the actual code:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;autoscale&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;():
    all_jobs = ORM.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;query_for_unfinished_jobs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()
    total_work = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;job &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;all_jobs:
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;job.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;is_valid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;():
            total_work = job.cost

    time_sla = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lookup_time_sla&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()
    worker_speed = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lookup_worker_speed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()
    ideal_worker_count = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;round&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(total_work &#x2F; (worker_speed * time_sla))

    min_workers, max_workers, actual_worker_count = AWS.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;describe_auto_scaling_group&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ideal_worker_count &amp;gt; max_workers:
        ideal_worker_count = max_workers

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ideal_worker_count &amp;lt; min_workers:
        ideal_worker_count = min_workers

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ideal_worker_count &amp;gt; actual_worker_count:
        PAAS.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;scale_up_to&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(ideal_worker_count)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;elif &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ideal_worker_count &amp;lt; actual_worker_count:
        PAAS.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;scale_down_to&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(ideal_worker_count)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The actual code has a few additional constraints and lots and lots of metric-spewing instrumentation so we can see what its thinking...all of which makes the code that much harder to reason about and all the more important to test!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-does-this-code-do&quot;&gt;What Does This Code Do?&lt;&#x2F;h3&gt;
&lt;p&gt;Essentially it does some math to decide how many servers we need to complete our long running batch jobs within a specific number of seconds (the &amp;quot;SLA&amp;quot;). Here&#x27;s the essential formula:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;number of workers = combined time estimates for all jobs &#x2F; (speed of one worker * the SLA)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Let&#x27;s mull over how changing the value of each individual variable in that formula effects the number of workers:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;As the total amount of work increases (all else being constant), we&#x27;ll need more workers&lt;&#x2F;li&gt;
&lt;li&gt;As the speed of the workers increases, we&#x27;ll need fewer workers&lt;&#x2F;li&gt;
&lt;li&gt;If we want the work to get done faster (by decreasing the SLA), we&#x27;ll need more workers&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So far so good! Those outcomes all seem reasonable and intuitive. The gist of the formula feels right.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;oh-the-edge-cases&quot;&gt;Oh, the Edge Cases!&lt;&#x2F;h3&gt;
&lt;p&gt;BUutttttt -- there&#x27;s a whole lot of additional concerns we need to think about to ensure this code is getting us to the correct outcomes. For one thing, some jobs might be invalid, so we&#x27;d want to exclude them from the calculation. For another, scaling up and scaling down work totally differently because we want a &lt;em&gt;graceful&lt;&#x2F;em&gt; shutdown, not interrupting a job in progress. Finally, we set boundaries on the min and max sizes of our pool of workers. If the ideal number of workers is more than the limit, we need to respect the limit. (In real life, there&#x27;s at least three other constraints we need to adhere to as well.) If we get this formula or its edge cases wrong, we&#x27;re either wasting money or making our customers frustrated. Its safe to say the stakes are high!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;testing-this-is-terrible&quot;&gt;Testing This Is Terrible&lt;&#x2F;h2&gt;
&lt;p&gt;So, how do I test the scale up and scale down behavior in various scenarios? There&#x27;s no arguments to this function and there&#x27;s no return values. However, you&#x27;ll notice that there&#x27;s basically one main mathy thing going on here to calculate the &lt;code&gt;ideal_worker_count&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s the consider the case of a cold start. I have no active workers, and suddenly a whole slew of jobs come in at once. Let&#x27;s check (1) we scale up and (2) we scale up to the correct amount. Given the above implementation, it would look something like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;@patch.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PAAS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;scale_up_to&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;)
@patch.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;PAAS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;scale_down_to&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;)
@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;patch&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;path.to.code.lookup_time_sla&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;return_value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;300&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;patch&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;path.to.code.lookup_worker_speed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;return_value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
@patch.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;AWS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;describe_auto_scaling_group&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;)
@patch.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ORM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;query_for_unfinished_jobs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;test_scale_up&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;m_query_for_unfinished_jobs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;m_describe_auto_scaling_group&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;m_lookup_worker_speed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;m_lookup_time_sla&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;m_scale_down_to&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;m_scale_up_to&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;):
    job = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Mock&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cost&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;120&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
    job.is_valid.return_value = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;True
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;m_query_for_unfinished_jobs.return_value = [job &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;range&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)]
    m_describe_auto_scaling_group.return_value = (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;autoscale&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# System under test

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;m_scale_up_to.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;assert_called_once_with&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This test stinks. Its so counterintuitive, we literally wrote out comments with the math spelled out as grade-school math homework. While I&#x27;m quite happy to mock out lots of things in my unit tests like this, how each of the different mocks interacts to reach that result of &lt;code&gt;2&lt;&#x2F;code&gt; at the bottom is extremely non-obvious. Now imagine around 10 different tests that are variations of this code for the various scenarios and intuitions I&#x27;ve outline for this piece of code. Good luck trying to debug this or extend it in the future!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;there-must-be-a-better-way&quot;&gt;There Must Be a Better Way&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s step back a second and think about what we&#x27;re really trying to do here in the test: We want to know that given some set of jobs and some set of workers, we arrive at the correct final size. It&#x27;s almost like all the various bits of information &lt;em&gt;should&lt;&#x2F;em&gt; be arguments to a function, and the size of the group should be the return value.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s forget about &lt;em&gt;where&lt;&#x2F;em&gt; all the data comes from and let&#x27;s focus on seeing what a function would look like that just did some math instead of finding all that data. Here goes:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;autoscale_math&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;min_workers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;max_workers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;total_work&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;worker_speed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;time_sla&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;):
    ideal_worker_count = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;round&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(total_work &#x2F; (worker_speed * time_sla))

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ideal_worker_count &amp;gt; max_workers:
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;max_workers

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ideal_worker_count &amp;lt; min_workers:
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;min_workers

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ideal_worker_count
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This code almost feels &lt;em&gt;too&lt;&#x2F;em&gt; terse to qualify for being its own separate function. But, how do the tests look for this?&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Let&amp;#39;s keep these bits constant for our test
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MIN_WORKERS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MAX_WORKERS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;WORKER_SPEED &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;TIME_SLA &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;300

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;test_scale_up&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;():
    current_workers = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;total_work = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1200

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ideal_worker_count = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;autoscale_math&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MIN_WORKERS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MAX_WORKERS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, total_work, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;WORKER_SPEED&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;TIME_SLA&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;assert &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ideal_worker_count == &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Already, this seems quite a bit clearer for testing as well as for the application code itself. But, if we want to really cover a significant portion of the different cases, perhaps we could be even more expressive somehow.&lt;&#x2F;p&gt;
&lt;p&gt;What if we used test tables to layout all the different test cases? Let&#x27;s see how that would look in python:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MIN_WORKERS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MAX_WORKERS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;WORKER_SPEED &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;TIME_SLA &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;300

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;test_autoscale_cases&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;():
    test_cases = [
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# total_work | ideal_worker_count | description
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1200&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,                   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;cold scale up&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;),
        (       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,                   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;scale up partially&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;),
        (    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1000000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,                  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;only scale up to max&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;),
        (          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,                   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;scale down totally&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;),
        (        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;300&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,                   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;scale down partially&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;),
        (   -&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1000000&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,                   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;only scale down to min&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;),
    ]

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(total_work, ideal_worker_count, description) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;test_cases:
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;assert &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;autoscale_math&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MIN_WORKERS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MAX_WORKERS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, total_work,
                              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;WORKER_SPEED&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;TIME_SLA&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) == ideal_worker_count, description
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This style of testing is considered idiomatic in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;golang&#x2F;go&#x2F;wiki&#x2F;TableDrivenTests&quot;&gt;golang&lt;&#x2F;a&gt; and is also my favorite feature of &lt;a href=&quot;https:&#x2F;&#x2F;docs.cucumber.io&#x2F;gherkin&#x2F;reference&#x2F;#scenario-outline&quot;&gt;Gherkins&lt;&#x2F;a&gt;. I don&#x27;t see a lot of python projects using this style, but it definitely works toward the overarching goal of most python syntax: readability.&lt;&#x2F;p&gt;
&lt;p&gt;The test tables allow me to think only about the specific case I want to test without thinking about the boilerplate to drive the test into that state. Combining that style of test with a very simple, &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Pure_function&quot;&gt;pure function&lt;&#x2F;a&gt; results in super easy test coverage for critical systems like this.&lt;&#x2F;p&gt;
&lt;p&gt;Notice that my test table doesn&#x27;t include coverage of the invalid jobs -- that&#x27;s okay! I&#x27;ve managed to compress so much boilerplate by using this test table, that I&#x27;ll just add another one-off test for the invalid job edge case. Trying to shoe-horn that bit into the above table wouldn&#x27;t really make sense to me because I&#x27;d have to add a column to every row, but really invalid jobs would only effect one of the tests.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;but-about-all-that-glue-code&quot;&gt;But About All That Glue Code?&lt;&#x2F;h3&gt;
&lt;p&gt;Right! So lets&#x27; try breaking about all the network calls into its own function and see if that makes any sense:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;autoscale&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;():
    time_sla = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lookup_time_sla&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()
    worker_speed = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lookup_worker_speed&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()
    min_workers, max_workers, current_workers = AWS.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;describe_auto_scaling_group&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()
    jobs = ORM.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;query_for_unfinished_jobs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()
    total_work = [job.cost &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;job &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;jobs &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;job.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;is_valid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()]

    ideal_worker_count = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;autoscale_math&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(current_workers, min_workers, max_workers, total_work, worker_speed, time_sla)

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ideal_worker_count &amp;gt; current_workers
        PAAS.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;scale_up_to&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(ideal_worker_count)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;elif &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ideal_worker_count &amp;lt; current_workers
        PAAS.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;scale_down_to&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(ideal_worker_count)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This function has also become dramatically simpler. Looking at this function, its clear that we also need to think about the failure modes for these calls as well as the business logic. For instance, should we retry the &lt;code&gt;PAAS.scale_up_to()&lt;&#x2F;code&gt; calls if they fail at first? Do we want a hardocded fallback for &lt;code&gt;lookup_worker_speed()&lt;&#x2F;code&gt;? I&#x27;ll leave those details as an exercise to the reader as well.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;when-separating-logic-and-io-does-make-sense&quot;&gt;When Separating Logic and IO Does Make Sense&lt;&#x2F;h2&gt;
&lt;p&gt;The key feature that made separating the IO and logic work for this case was that we want all the data from various network calls every time. As long as we need to always gather the same data every time (or doing that IO is considered cheap in your context), then we don&#x27;t have to be strategic about when to gather the data. We just &lt;em&gt;always&lt;&#x2F;em&gt; get all of it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;when-separating-logic-and-io-doesn-t-make-sense&quot;&gt;When Separating Logic and IO Doesn&#x27;t Make Sense&lt;&#x2F;h2&gt;
&lt;p&gt;Sometimes, various networks calls which are &lt;em&gt;input&lt;&#x2F;em&gt; to your buisness logic can and should be skipped. In those cases, the refactor technique only makes complex code harder to read. If we try to pull all of the IO out but still be strategic, now we end up making lots of little functions to evaluate lazily. If you strongly prefer very short and numerous 1-2 lines functions over 100-200 line functions, maybe separating the IO and Logic still makes sense. However, when I&#x27;ve tried this, my coworkers didn&#x27;t thank me and there was just a lot more code to try to understand. Additionally, to make the network calls lazy, I had all more functions which just added more boilerplate to sift through. The end result was greater head scratching.&lt;&#x2F;p&gt;
&lt;p&gt;For these cases, there&#x27;s a number of refactor strategies you could try:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Look for dead code that can be totally removed&lt;&#x2F;li&gt;
&lt;li&gt;Simplify your data model to reduce the differences in these various scenarioes&lt;&#x2F;li&gt;
&lt;li&gt;Use some kind of caching to make the data less expensive to look up&lt;&#x2F;li&gt;
&lt;li&gt;Consider breaking apart a series of smaller chunks of IO + logic units and chain them together&lt;&#x2F;li&gt;
&lt;li&gt;Try using a cucmber clone to write narrative descriptive tests around the icky bits&lt;&#x2F;li&gt;
&lt;li&gt;What component has too many responsibilites? Can I split out one muddled abstraction into two more narrow abstractions?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Sometimes, the nasty code refuses to yield to your gentle coaxing, and that&#x27;s okay too.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Sometimes, separating your core logic out from IO can greatly enhance the readability and testability of your code. Try using test tables to greatly reduce amount of boilerplate you need to write. Also, make sure that your code always fetches the same data before trying to isolate logic and network concerns.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Recurse Center Day 5: Wrapping Up</title>
		<published>2018-01-12T00:00:00+00:00</published>
		<updated>2018-01-12T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/recurse-center-day-5-wrapping-up/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/recurse-center-day-5-wrapping-up/</id>
		<content type="html">&lt;p&gt;I want to learn more about how video codecs work so someday I can grow up to be a Jedi-master Video Engineer. To aid me on this epic quest, I&#x27;m doing the one week &amp;quot;mini-batch&amp;quot; at the &lt;a href=&quot;https:&#x2F;&#x2F;www.recurse.com&#x2F;&quot;&gt;Recurse Center&lt;&#x2F;a&gt;. Here&#x27;s my notes from Day 5.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;late-night-reflections&quot;&gt;Late Night Reflections&lt;&#x2F;h2&gt;
&lt;p&gt;Last night, I was chatting with three other programmers from RC and Geoffrey said somethings that got lodged in my head:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;athletes-with-no-practice&quot;&gt;Athletes with no practice&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Can you imagine being an athlete that never practiced and only played real games? And yet that&#x27;s what we do in software development. We never practice the basic skills.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Then he recounted how one of his friends was practicing how to quickly setup a new angular app from scratch. The friend would schedule 90 minutes to do &amp;quot;reps&amp;quot;, and give himself 15 minutes to go from 0 code to a running angular app. At the end of the 15 minutes, he delete the app and start again. Then he&#x27;d stop after the 90 minutes were over.&lt;&#x2F;p&gt;
&lt;p&gt;Geoffrey said, &amp;quot;Why don&#x27;t practice code review and team communication and other skills too? I think I&#x27;m going to write a new lisp interpreter every three months to keep practising.&amp;quot; I feel like this was one of those watershed moments where the way I think about how to improve has radically altered.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;failing-to-learn-from-failure&quot;&gt;Failing to learn from failure&lt;&#x2F;h3&gt;
&lt;p&gt;One last profound thought from Geoffrey last night:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Failing to learn from failure is the only unforgivable failure.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;While I totally agree with that sentiment, I feel like that&#x27;s a thought I need to chew on and try to internalize. Reflecting on my own decisions and actions isn&#x27;t something that comes naturally, but it feels like a skill I ought to focus on.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pair-blogging&quot;&gt;Pair Blogging&lt;&#x2F;h3&gt;
&lt;p&gt;While chatting with these recursers, I came up with the idea of &amp;quot;pair blogging&amp;quot;: it&#x27;s pair programming except that the &amp;quot;navigator&amp;quot; is responsible to live blog what the pair is programming, code samples, screenshots, problems, design decisions, links to relevant documentation etc. Then when the pair switch roles, the other person writes &amp;quot;Ok, Bob writing now...&amp;quot; and the other person takes over writing. Not sure if it&#x27;s a terrible idea or an amazing idea...&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-intentions&quot;&gt;Setting Intentions&lt;&#x2F;h2&gt;
&lt;p&gt;Today, I&#x27;m going to practice timeboxing work. I&#x27;ll write a repl explorer for an mkv file in 30 minutes, three times (doing &amp;quot;reps&amp;quot;), and then after lunch I&#x27;ll do other projects: timebox trying to compile to web assembly to an hour, and do three half-hour reps of using bindgen to make rust bindings for libvpx.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;doing-reps-making-an-mkv-repl&quot;&gt;Doing Reps - Making an mkv repl&lt;&#x2F;h2&gt;
&lt;p&gt;Doing this project 3 times in a row was just amazing. I never quite got it working all the way, but I&#x27;ve gotten much better at making the repl itself. The difference between rep 1 and rep 2 as immense. I was able to really get good at making the repl itself with multiple commands in an idiomatic Rust fashion. I&#x27;m really proud of my work in rep 2.&lt;&#x2F;p&gt;
&lt;p&gt;At the end of rep 2, I had barely started figuring out how to interactively traverse the tree of EMBL Elements, so rep 3 (which was only 15 min) focused on that. I&#x27;ll need several more reps to figure it out... The whole ownership &#x2F; &lt;code&gt;Rc&lt;&#x2F;code&gt; data structure got in the way. I was fighting the borrow checker the whole. I think, fundamentally I just don&#x27;t understand how &lt;code&gt;Rc&lt;&#x2F;code&gt;&#x27;s work at all -- which is really embarrassing after having so much Rust for fun.&lt;&#x2F;p&gt;
&lt;p&gt;See the code from &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;bff&#x2F;reps-mkv-repl&quot;&gt;all three mkv-repl reps on gitlab&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Honestly, 3 reps was not enough to have making a repl down pat, let alone figure out how to navigate the tree while appeasing the borrow checker. I really want to do this more in the future.&lt;&#x2F;p&gt;
&lt;p&gt;Having a wife and kids who I want and need to spend most of my nonwork hours with, one of the main frustrations I face on a regular basis is that there&#x27;s nothing meaningful I can do with the scraps of 30 minute blocks of time I have. Now, with reps I have a new way to feel good about doing a mini project on a regular basis that will let me improve and fit into the time I actually have available.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;compiling-rust-to-webassembly-wasm&quot;&gt;Compiling Rust to WebAssembly (wasm)&lt;&#x2F;h2&gt;
&lt;p&gt;Okay, I&#x27;ve got one hour...where do I even start here? This is &lt;em&gt;so&lt;&#x2F;em&gt; crazy! I remember there was blog from &lt;a href=&quot;https:&#x2F;&#x2F;blog.rust-lang.org&#x2F;&quot;&gt;Rust lang&lt;&#x2F;a&gt; about doing recently. Nope, I can&#x27;t see any posts about this.. Let&#x27;s duckduckgo for it. There&#x27;s a post for Rust 1.14 back in 2016...no way it was that long ago. Uh, yep it was that long ago. Let&#x27;s find the &lt;a href=&quot;https:&#x2F;&#x2F;users.rust-lang.org&#x2F;t&#x2F;compiling-to-the-web-with-rust-and-emscripten&#x2F;7627&quot;&gt;really really basic example&lt;&#x2F;a&gt; of how to do this from the Rust team(s):&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;h4 id=&quot;rust-installation&quot;&gt;Rust installation:&lt;&#x2F;h4&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;rustup target add wasm32-unknown-emscripten
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you already have rustup installed, then instead of running the curl and
source commands above, just switch to the nightly toolchain with rustup default nightly.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;emscripten-installation&quot;&gt;Emscripten installation:&lt;&#x2F;h4&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;curl -O https:&#x2F;&#x2F;s3.amazonaws.com&#x2F;mozilla-games&#x2F;emscripten&#x2F;releases&#x2F;emsdk-portable.tar.gz
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;tar -xzf emsdk-portable.tar.gz
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;source emsdk_portable&#x2F;emsdk_env.sh
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;emsdk update
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;emsdk install sdk-incoming-64bit
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;emsdk activate sdk-incoming-64bit
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;blockquote&gt;
&lt;p&gt;I have rust already installed, I added toolchain...do I really need to install emscripten separately? Let&#x27;s just try compiling to the wasm target without installing emscripten:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;echo &amp;#39;fn main() { println!(&amp;quot;Hello, Emscripten!&amp;quot;); }&amp;#39; &amp;gt; hello.rs
rustc --target=asmjs-unknown-emscripten hello.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;No love. Under the hood, &lt;code&gt;rustc&lt;&#x2F;code&gt; is shelling out to &lt;code&gt;emcc&lt;&#x2F;code&gt; (presumably the emscripten compiler) which is not found on my system. Ugh, okay, actually install empscripten. Step 1, curl a thing - check. Step 2, untar a thing - check. Step 3, source a thing - nope. There&#x27;s a typo -- it should be &lt;code&gt;source emsdk-portable&#x2F;emsdk_env.sh&lt;&#x2F;code&gt;. Check. Step 4, update a think, check. Step 4, install the 64 bit sdk, check.....nope, this taking forever. Omg, its still going. Still going. I&#x27;m only 55% done and its been running for approximately 35 minutes. Not sure this is going to work out...good thing I have a blog to finish up.&lt;&#x2F;p&gt;
&lt;p&gt;It feels like I should be doing something to help to actually write the code and compile to wasm while I wait...but I don&#x27;t want to forget to watch for the compilation to finish or I&#x27;ll not get anywhere in the 19:05 let on my timer.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s lookup &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;cargo&#x2F;reference&#x2F;manifest.html&quot;&gt;how to set the target&lt;&#x2F;a&gt; for a project in the Cargo.toml file. Arg, I can&#x27;t find anything about how to tell cargo which platform to target. Let&#x27;s check the &lt;code&gt;cargo help&lt;&#x2F;code&gt; command...Great! &lt;code&gt;cargo build --help&lt;&#x2F;code&gt; tells that the magic flag is &lt;code&gt;--target&lt;&#x2F;code&gt; and I need to put the target triple after that. I know &lt;code&gt;rustup toolchain list&lt;&#x2F;code&gt; can tell what the target triple is for wasm...just kidding &lt;code&gt;rustup target list&lt;&#x2F;code&gt; it is. The target is &lt;code&gt;wasm32-unknown-emscripten&lt;&#x2F;code&gt;...which is what I copied from the rust user forum above. Perfect!&lt;&#x2F;p&gt;
&lt;p&gt;78% of emscripten is build...only 10 minutes left for my hour time box...&lt;&#x2F;p&gt;
&lt;p&gt;What else can I do? I supposed I could start writing something that wraps the mkv crate, but if the hello world code doesn&#x27;t compile whats the point?&lt;&#x2F;p&gt;
&lt;p&gt;79% compiled....&lt;&#x2F;p&gt;
&lt;p&gt;80% compiled...&lt;&#x2F;p&gt;
&lt;p&gt;Hmm, I guess I could look at code samples that suspected to work with wasm...Okay, here&#x27;s this &lt;a href=&quot;https:&#x2F;&#x2F;hackernoon.com&#x2F;compiling-rust-to-webassembly-guide-411066a69fde&quot;&gt;great post about baby steps in Rust -&amp;gt; wasm&lt;&#x2F;a&gt; which is the same as I&#x27;ve done (but much better written). It took him 2hrs to compile...so either my compilation is actually &lt;em&gt;very&lt;&#x2F;em&gt; fast, or there&#x27;s whole other pass after the 81% finished job is done. Anyway, the author suggests enabling a config flag in firefox...let&#x27;s do that! Here&#x27;s my about:config default settings:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2018&#x2F;firefox-57-aboutconfig-wasm.png&quot; alt=&quot;Firefox 57 about:config defaults for Web Assembly&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;84% compiled...less than 1 minute to go&lt;&#x2F;p&gt;
&lt;p&gt;Okay, I&#x27;ll conceded my time is up. Oh well! I knew it was a long shot. Next time I want to try this, I&#x27;ll have emscripten all ready to go (hopefully!).&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Recurse Center Day 4: No, really I am going to decode a frame</title>
		<published>2018-01-11T00:00:00+00:00</published>
		<updated>2018-01-11T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/recurse-center-day-4-no-really-i-am-going-to-decode-a-frame/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/recurse-center-day-4-no-really-i-am-going-to-decode-a-frame/</id>
		<content type="html">&lt;p&gt;I want to learn more about how video codecs work so someday I can grow up to be a Jedi-master Video Engineer. To aid me on this epic quest, I&#x27;m doing the one week &amp;quot;mini-batch&amp;quot; at the &lt;a href=&quot;https:&#x2F;&#x2F;www.recurse.com&#x2F;&quot;&gt;Recurse Center&lt;&#x2F;a&gt;. Here&#x27;s my notes from Day 4.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;late-night-brainstorming&quot;&gt;Late Night Brainstorming&lt;&#x2F;h2&gt;
&lt;p&gt;So, on my walk home last night, I had a whole flood of new ideas for this project come into my head. Here&#x27;s a quick overview of some of the most interesting ones:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Make an interactively GDB-style repl for exploring the elements in a Matroska container - I could almost definitely do this in a few hours&lt;&#x2F;li&gt;
&lt;li&gt;Compile to WebAssembly and create a client-side Matroska parser that can:
&lt;ul&gt;
&lt;li&gt;present a tree explorer using [+] and [-] buttons, like a bit like Finder&lt;&#x2F;li&gt;
&lt;li&gt;create a visualization of frame size along a timeline, possibly passing the same data to a video element and seeking to the frame on hover&lt;&#x2F;li&gt;
&lt;li&gt;decode vpx frames, analyze frame data and visualize them and show them in a canvas&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Some mashup of these things&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;setting-intentions-for-the-day&quot;&gt;Setting Intentions for the Day&lt;&#x2F;h2&gt;
&lt;p&gt;Okay, so I&#x27;m committing to digging into decoding a frame. I feel like if I&#x27;m willing to properly ask for help, so many more interesting things will be unlocked for me. Plus...I&#x27;ve told literally everyone that&#x27;s what I want to do! &lt;em&gt;After&lt;&#x2F;em&gt; I finish that (and maybe only that today), then I&#x27;ll try compiling to WebAssembly and doing fun things in the browser.&lt;&#x2F;p&gt;
&lt;p&gt;A few duckduckduckgo&#x27;s later, I find this super helpful looking &lt;a href=&quot;https:&#x2F;&#x2F;chromium.googlesource.com&#x2F;webm&#x2F;bitstream-guide&#x2F;+&#x2F;master&#x2F;text_src&#x2F;atch1&#x2F;vpx_decoder.h&quot;&gt;header file in bitstream guide&lt;&#x2F;a&gt;. Hmm, after reading that it still seems like I want to be looking at simple_decoder.c to grok libvpx and perhaps just use the header file as a bit of documentation about the data structures.&lt;&#x2F;p&gt;
&lt;p&gt;My goal is to simply rewrite simple_decoder.c so that I can just feed it a keyframe (I&#x27;ll figure out how to get just one keyframe later on).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rewriting-simple-decoder-c-in-c&quot;&gt;Rewriting simple_decoder.c in C&lt;&#x2F;h2&gt;
&lt;p&gt;Right now, I&#x27;m totally buggered by all the video_reader_* stuff coming out of video_reader.c. Rewriting needs to entirely replace that code.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;0-can-i-recompile-it&quot;&gt;0 - Can I recompile it?&lt;&#x2F;h3&gt;
&lt;p&gt;Since this is stored in git, I can just edit that file. Let&#x27;s see make sure &lt;em&gt;really&lt;&#x2F;em&gt; do know how to recompile this file. I&#x27;ll add a gratuitious print statement at the beginning and just try to run that. I add a &lt;code&gt;die(&amp;quot;Hello!&amp;quot;)&lt;&#x2F;code&gt; as the first line in &lt;code&gt;main()&lt;&#x2F;code&gt;, and then recompile it like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ cd build
$ make
make[1]: Nothing to be done for `all&amp;#39;.
    [DEP] examples&#x2F;simple_decoder.c.d
    [CC] examples&#x2F;simple_decoder.c.o
    [LD] examples&#x2F;simple_decoder
make[1]: Nothing to be done for `all&amp;#39;.
make[1]: Nothing to be done for `all&amp;#39;.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Great! Make was smart enough to only recompiled simple_decoder for me. Fantastic! Let&#x27;s see it work:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ .&#x2F;examples&#x2F;simple_decoder
Hello!
Usage: (null) &amp;lt;infile&amp;gt; &amp;lt;outfile&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s odd -- well, my code &lt;em&gt;clearly&lt;&#x2F;em&gt; ran, but the program didn&#x27;t run the way I expected at all. I thought that &lt;code&gt;die()&lt;&#x2F;code&gt; would actually stop execution, but we still saw the usage info code being run, so that just proves I don&#x27;t know C.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-figure-out-video-reader-is-doing&quot;&gt;1 - Figure out video_reader is doing&lt;&#x2F;h3&gt;
&lt;p&gt;The main loop of the program looks like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;  while (vpx_video_reader_read_frame(reader)) {
    vpx_codec_iter_t iter = NULL;
    vpx_image_t *img = NULL;
    size_t frame_size = 0;
    const unsigned char *frame =
        vpx_video_reader_get_frame(reader, &amp;amp;frame_size);
    if (vpx_codec_decode(&amp;amp;codec, frame, (unsigned int)frame_size, NULL, 0))
      die_codec(&amp;amp;codec, &amp;quot;Failed to decode frame.&amp;quot;);
    while ((img = vpx_codec_get_frame(&amp;amp;codec, &amp;amp;iter)) != NULL) {
      vpx_img_write(img, outfile);
      ++frame_cnt;
    }
  }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s two calls to &lt;code&gt;video_reader_*&lt;&#x2F;code&gt; things in this part:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;while (vpx_video_reader_read_frame(reader)) {&lt;&#x2F;code&gt; - just from squinting at this code, it looks to me like the &lt;code&gt;reader&lt;&#x2F;code&gt; contains all the state, and &lt;code&gt;vpx_video_reader_read_frame&lt;&#x2F;code&gt; must be returning false or NULL whenever &lt;code&gt;reader&lt;&#x2F;code&gt; has reached the end, possibly returning something truthy before then.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;const unsigned char *frame = vpx_video_reader_get_frame(reader, &amp;amp;frame_size);&lt;&#x2F;code&gt; - a few observations here:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;frame_size&lt;&#x2F;code&gt; is initialized as 0, but apparently is nonzero later on. So, it must be passed by reference so that &lt;code&gt;vpx_video_reader_get_frame&lt;&#x2F;code&gt; can mutate it...presumably because multiple return values is hard in C&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;frame&lt;&#x2F;code&gt; holds all the coded data&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So, theoretically, if I could hardcode the values of &lt;code&gt;frame_size&lt;&#x2F;code&gt; and &lt;code&gt;frame&lt;&#x2F;code&gt;, I could remove all the &lt;code&gt;video_reader_*&lt;&#x2F;code&gt; things. Great! Let&#x27;s try printing those things out in the shell on the first iteration, crash the program. So the main loop becomes:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;  while (vpx_video_reader_read_frame(reader)) {
    vpx_codec_iter_t iter = NULL;
    vpx_image_t *img = NULL;
    size_t frame_size = 0;
    const unsigned char *frame =
        vpx_video_reader_get_frame(reader, &amp;amp;frame_size);

    printf(&amp;quot;frame_size=%d&amp;quot;, frame_size);  &#x2F;&#x2F; THIS IS PRINT STATEMENT!

    if (vpx_codec_decode(&amp;amp;codec, frame, (unsigned int)frame_size, NULL, 0))
      die_codec(&amp;amp;codec, &amp;quot;Failed to decode frame.&amp;quot;);
    while ((img = vpx_codec_get_frame(&amp;amp;codec, &amp;amp;iter)) != NULL) {
      vpx_img_write(img, outfile);
      ++frame_cnt;
    }
  }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now the compiler complains that my format string is wrong...and has helpfully suggested replacing &lt;code&gt;%d&lt;&#x2F;code&gt; with &lt;code&gt;%zu&lt;&#x2F;code&gt; (whatever that means!). Great! Now I can frame_size being printed. Let&#x27;s try that again for the &lt;code&gt;frame&lt;&#x2F;code&gt; variable too...apparently the right format string is &lt;code&gt;%s&lt;&#x2F;code&gt; (I actually know that one from golang and python!). Also, I can&#x27;t imagine that string is the right way to visualize encoded binary data...and it&#x27;s not. I see nasty unicode things in the terminal (but it did compile).&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s actually stop and learn about C-style format strings. A quick skim through &lt;code&gt;man printf&lt;&#x2F;code&gt; shows that there&#x27;s a &lt;code&gt;%b&lt;&#x2F;code&gt; format which is like &lt;code&gt;%s&lt;&#x2F;code&gt; but escapes differently (including octal) -- but that doesn&#x27;t produce the output I want. There&#x27;s also a &lt;code&gt;%X&lt;&#x2F;code&gt; for octal but it only prints the first octet, not the entirety of &lt;code&gt;frame&lt;&#x2F;code&gt;. No idea how to do this, time to duckduckgo how to print out binary data from C in printf. So, it looks like &lt;a href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;13275258&#x2F;printing-char-buffer-in-hex-array#13275307&quot;&gt;we can iterate over frame and print each value as %x&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;printf(&amp;quot;\nframe_size=%zu | frame=\n&amp;quot;, frame_size);
for (int i = 0; i &amp;lt; frame_size; i++) {
    printf(&amp;quot;%02x&amp;quot;, frame[i]);
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Amazing! So I wind up with things like this as output:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;frame_size=1650 | frame=
114d001d10e4147b8c4fd3c78f2713ff7daac8....&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Now, I need to figure out how I would actually create a value I would copy and paste into a C program as valid syntax...Maybe I&#x27;ll ask for help from RC after lunch.&lt;&#x2F;p&gt;
&lt;p&gt;Man, I&#x27;m dragging! Okay, well let&#x27;s just try writing a frame to a file as binary. Phew -- how do I write binary to a file in C? Well I know that this example program already does that, writing each frame as &amp;quot;image&amp;quot; into the &lt;code&gt;outfile&lt;&#x2F;code&gt; argument. So, &lt;a href=&quot;https:&#x2F;&#x2F;chromium.googlesource.com&#x2F;webm&#x2F;libvpx&#x2F;+&#x2F;master&#x2F;examples&#x2F;simple_decoder.c#133&quot;&gt;tracing through the code&lt;&#x2F;a&gt; I find this call to &lt;a href=&quot;https:&#x2F;&#x2F;chromium.googlesource.com&#x2F;webm&#x2F;libvpx&#x2F;+&#x2F;master&#x2F;tools_common.c#231&quot;&gt;&lt;code&gt;fwrite&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;    const unsigned char *buf = img-&amp;gt;planes[plane];
    const int stride = img-&amp;gt;stride[plane];
    const int w = vpx_img_plane_width(img, plane) *
                  ((img-&amp;gt;fmt &amp;amp; VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
    const int h = vpx_img_plane_height(img, plane);
    int y;
    for (y = 0; y &amp;lt; h; ++y) {
      fwrite(buf, 1, w, file);
      buf += stride;
    }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Omg, that&#x27;s far too many parameters for a write function. What do all these mean? Ahhh, I&#x27;m in C, so &lt;code&gt;man fwrite&lt;&#x2F;code&gt; (redacted below) is my friend:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;h3 id=&quot;name&quot;&gt;NAME&lt;&#x2F;h3&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt; fread, fwrite -- binary stream input&#x2F;output
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;synopsis&quot;&gt;SYNOPSIS&lt;&#x2F;h3&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt; #include &amp;lt;stdio.h&amp;gt;
 size_t
 fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream);
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h4 id=&quot;description&quot;&gt;DESCRIPTION&lt;&#x2F;h4&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt; The function fwrite() writes nitems objects, each size bytes long, to the stream pointed to by stream, obtaining them
 from the location given by ptr.
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h4 id=&quot;return-values&quot;&gt;RETURN VALUES&lt;&#x2F;h4&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt; The function fread() does not distinguish between end-of-file and error; callers must use feof(3) and ferror(3) to
 determine which occurred.  The function fwrite() returns a value less than nitems only if a write error has occurred.
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;blockquote&gt;
&lt;p&gt;Shockingly helpful, that. So going back to my &lt;code&gt;fwrite&lt;&#x2F;code&gt; example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;fwrite(buf, 1, w, file);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;buf&lt;&#x2F;code&gt; - this is a pointer that I&#x27;m going to copy &lt;em&gt;from&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;1&lt;&#x2F;code&gt; - is the size in bytes of each &amp;quot;item&amp;quot; I&#x27;m going to take take from &lt;code&gt;buf&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;w&lt;&#x2F;code&gt; - is the number of &amp;quot;items&amp;quot; to read from &lt;code&gt;buf&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;file&lt;&#x2F;code&gt; - is the destination where all those items are being written &lt;em&gt;to&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Not so scary now. Let&#x27;s go back and try to write my frame out to a file:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;fwrite(
  frame,      &#x2F;&#x2F; (replaces buf) this is the raw frame data I want to store in a file
  1,          &#x2F;&#x2F; (stays the same)
  frame_size, &#x2F;&#x2F; (replaces w) this is the number of bytes in frame, all of which I want to write
);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Okay, so I&#x27;ve now rewritten my inner loop to be a conditional (so that it only runs once) that looks like this now:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;  if (vpx_video_reader_read_frame(reader)) {
    vpx_codec_iter_t iter = NULL;
    vpx_image_t *img = NULL;
    size_t frame_size = 0;
    const unsigned char *frame =
        vpx_video_reader_get_frame(reader, &amp;amp;frame_size);

    printf(&amp;quot;\nframe_size=%zu\n&amp;quot;, frame_size);
    fwrite(frame, 1, frame_size, outfile);
  }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Compile and run against my IVF file and it appears to have worked! Here&#x27;s a screenshot of the outfile inside &lt;a href=&quot;http:&#x2F;&#x2F;ridiculousfish.com&#x2F;hexfiend&#x2F;&quot;&gt;Hex Fiend&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2018&#x2F;hex-fiend-of-raw-vp8-frame.png&quot; alt=&quot;Hex Fiend of Raw VP8 frame&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can see that the number of bytes from my hacked-up C program and Hex Fiend are both 34974 bytes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pair-programming-iterlude&quot;&gt;Pair Programming Iterlude&lt;&#x2F;h2&gt;
&lt;p&gt;My fellow Recurser, Andy, offered to pair with me on figuring out all this and we were able to get a simplified C version of simple_decoder working:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;
int main(int argc, char **argv) {
  int frame_cnt = 0;
  FILE *infile = NULL;
  vpx_codec_ctx_t codec;
  const VpxInterface *decoder = NULL;

  if (argc != 2) die(&amp;quot;Invalid number of arguments.&amp;quot;);

  if (!(infile = fopen(argv[1], &amp;quot;rb&amp;quot;)))
    die(&amp;quot;fread: Failed to open %s for reading&amp;quot;, argv[2]);

  decoder = get_vpx_decoder_by_fourcc(0x30385056); &#x2F;&#x2F;info-&amp;gt;codec_fourcc);
  if (!decoder) die(&amp;quot;Unknown input codec.&amp;quot;);

  if (vpx_codec_dec_init(&amp;amp;codec, decoder-&amp;gt;codec_interface(), NULL, 0))
    die_codec(&amp;amp;codec, &amp;quot;Failed to initialize decoder.&amp;quot;);

  vpx_codec_iter_t iter = NULL;
  vpx_image_t *img = NULL;
  size_t frame_size = 34974;
  const unsigned char *frame = malloc(frame_size);

  if(fread(frame, 1, frame_size, infile) != frame_size) die(&amp;quot;Nope&amp;quot;);

  if (vpx_codec_decode(&amp;amp;codec, frame, (unsigned int)frame_size, NULL, 0))
    die_codec(&amp;amp;codec, &amp;quot;Failed to decode frame.&amp;quot;);

  while ((img = vpx_codec_get_frame(&amp;amp;codec, &amp;amp;iter)) != NULL) {
    ++frame_cnt;
  }

  printf(&amp;quot;Processed %d frames.\n&amp;quot;, frame_cnt);
  if (vpx_codec_destroy(&amp;amp;codec)) die_codec(&amp;amp;codec, &amp;quot;Failed to destroy codec&amp;quot;);

  fclose(infile);
  return EXIT_SUCCESS;
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This program basically just reads in the raw binary from a previous file (&lt;code&gt;infile&lt;&#x2F;code&gt;) and has various configuration settings hardcoded for the decoder to be initialized and the frame size.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;it-still-doesn-t-work&quot;&gt;It still doesn&#x27;t work&lt;&#x2F;h2&gt;
&lt;p&gt;Unfortunately, the &lt;code&gt;libvpx_native_sys&lt;&#x2F;code&gt; doesn&#x27;t provide many of structs and things I need even to get this simplified simple_decoder to work.... I give up for today.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Debugging Rust programs with lldb on MacOS</title>
		<published>2018-01-10T00:00:00+00:00</published>
		<updated>2018-01-10T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/debugging-rust-programs-with-lldb/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/debugging-rust-programs-with-lldb/</id>
		<content type="html">&lt;p&gt;I&#x27;ll explain how to step through a Rust application using LLDB and illustrate some of the most basic commands. This post assumes MacOS throughout. Things are probably &lt;em&gt;very&lt;&#x2F;em&gt; different on Windows, but might be very similar on Linux and friends.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-debuggers-in-rust&quot;&gt;Why debuggers in Rust&lt;&#x2F;h2&gt;
&lt;p&gt;There&#x27;s no &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Read%E2%80%93eval%E2%80%93print_loop&quot;&gt;repl&lt;&#x2F;a&gt; for Rust, so its especially important to use other techniques to interact with Rust code; I believe that its unlikely there would ever be a Rust repl given the fact that Rust is a compiled language. Additionally, its very cumbersome to use the online &lt;a href=&quot;https:&#x2F;&#x2F;play.rust-lang.org&#x2F;&quot;&gt;playground&lt;&#x2F;a&gt; for understanding the code I&#x27;m actually working on right now. So, the main options for debugging code are (1) lean on the compiler and (2) lots of &lt;code&gt;println!()&lt;&#x2F;code&gt;, which can only get you so far.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, Rust has &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Application_binary_interface&quot;&gt;C Application Binary Interface (ABI)&lt;&#x2F;a&gt; compatibility so we can mostly pretend that compiled Rust &lt;em&gt;is&lt;&#x2F;em&gt; C code. The Rust compiler team has also embedded debug symbols using the &lt;a href=&quot;http:&#x2F;&#x2F;dwarfstd.org&#x2F;&quot;&gt;DWARF&lt;&#x2F;a&gt; protocol, so that means that we can use the always-fashionable &lt;a href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;gdb&#x2F;&quot;&gt;GDB&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;lldb.llvm.org&#x2F;&quot;&gt;LLDB&lt;&#x2F;a&gt;. Some of the formatting isn&#x27;t quite right, so Rust&#x27;s package manager &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;cargo&#x2F;&quot;&gt;cargo&lt;&#x2F;a&gt; ships with a wrapper script for each: &lt;code&gt;rust-gdb&lt;&#x2F;code&gt; and &lt;code&gt;rust-lldb&lt;&#x2F;code&gt; that includes a python script for formatting purposes. &lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-compile-with-debug-symbols&quot;&gt;1 - Compile with debug symbols&lt;&#x2F;h2&gt;
&lt;p&gt;By default, &lt;code&gt;cargo build&lt;&#x2F;code&gt; creates a &amp;quot;debug&amp;quot; build that contains the debug symbols. However, &lt;code&gt;cargo install&lt;&#x2F;code&gt; does a &amp;quot;release&amp;quot; build with is optimized and contains no debug symbols. I haven&#x27;t figured out to use LLDB with &lt;code&gt;cargo install&lt;&#x2F;code&gt;, but it&#x27;s probably very similar what I&#x27;m going in this post.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ cargo build -v
   Compiling mycrate v0.1.0 (file:&#x2F;&#x2F;&#x2F;home&#x2F;user&#x2F;programming&#x2F;mycrate)
     Running `rustc --crate-name mycrate src&#x2F;main.rs --crate-type bin --emit=dep-info,link -C debuginfo=2 -C metadata=8b3e6af7e4113faf -C extra-filename=-8b3e6af7e4113faf --out-dir &#x2F;home&#x2F;user&#x2F;programming&#x2F;mycrate&#x2F;target&#x2F;debug&#x2F;deps -L dependency=&#x2F;home&#x2F;user&#x2F;programming&#x2F;mycrate&#x2F;target&#x2F;debug&#x2F;deps --extern log=&#x2F;home&#x2F;user&#x2F;programming&#x2F;mycrate&#x2F;target&#x2F;debug&#x2F;deps&#x2F;liblog-6d2adb16c0d397ce.rlib --extern mkv=&#x2F;home&#x2F;user&#x2F;programming&#x2F;mycrate&#x2F;target&#x2F;debug&#x2F;deps&#x2F;libmkv-391472e7b11b31e6.rlib --extern env_logger=&#x2F;home&#x2F;user&#x2F;programming&#x2F;mycrate&#x2F;target&#x2F;debug&#x2F;deps&#x2F;libenv_logger-8b11da9f68ecb368.rlib`
    Finished dev [unoptimized + debuginfo] target(s) in 13.4 secs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notice the &lt;code&gt;Finished dev [unoptimized + debuginfo]...&lt;&#x2F;code&gt; on the last line. The &amp;quot;debuginfo&amp;quot; tells us we have produced a debugable binary.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;digression-compiling-debug-symbols-using-rustc-directly&quot;&gt;Digression - Compiling debug symbols using rustc directly&lt;&#x2F;h3&gt;
&lt;p&gt;If you don&#x27;t use &lt;code&gt;cargo&lt;&#x2F;code&gt; (I&#x27;m looking at you Dropbox!), the &lt;code&gt;rustc&lt;&#x2F;code&gt; compiler supports two command line flags which will do this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-g&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-C debuginfo=2&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As far as I can tell, these appear to do exactly the same thing. There&#x27;s no reason to pick one over the other. However, you may only use one or the other form but &lt;strong&gt;not both&lt;&#x2F;strong&gt;. Full example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;rustc -g src&#x2F;main.rs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;2-test-loading-your-program-in-lldb&quot;&gt;2 - Test loading your program in LLDB&lt;&#x2F;h2&gt;
&lt;p&gt;I got stuck here for quite a while, so I&#x27;m going make verifying the LLDB&#x2F;Rust setup a whole step in this tutorial.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Quick primer for folks new to LLDB&#x2F;GDB like me -- these debuggers are invoked directly from the command line and passed an executable that they will target. The executable is &lt;strong&gt;not&lt;&#x2F;strong&gt; started -- by default there are no breakpoints set, so program would just run to completion without ever letting LLDB pause and explore.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Okay, let&#x27;s feed LLDB our program &lt;code&gt;mainMYGITHASH&lt;&#x2F;code&gt; (which is in target&#x2F;debug&#x2F;deps&#x2F;), by typing:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ sudo rust-lldb target&#x2F;debug&#x2F;deps&#x2F;mainMYGITHASH
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Notice the path is target&#x2F;debug&#x2F;&lt;strong&gt;deps&lt;&#x2F;strong&gt;&#x2F;main&lt;strong&gt;MYGITHASH&lt;&#x2F;strong&gt; -- you must use the binary in the deps directory. See this &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;cargo&#x2F;issues&#x2F;4056#issue-228928448&quot;&gt;cargo issue&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;On MacOS, &lt;code&gt;sudo&lt;&#x2F;code&gt; seems to be required, otherwise I see errors about &amp;quot;codesigned&amp;quot; and nothing works. I kind of suspect Linux and friends are similar.&lt;&#x2F;li&gt;
&lt;li&gt;On my machine, &lt;code&gt;lldb&lt;&#x2F;code&gt; is already installed though I have no idea if this was accidental or ships with MacOS by default. Linux systems may need install it, but I have no idea&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;rustup.rs&#x2F;&quot;&gt;Rustup&lt;&#x2F;a&gt; installs &lt;code&gt;rust-lldb&lt;&#x2F;code&gt; in $HOME&#x2F;.cargo&#x2F;bin alongside &lt;code&gt;rustc&lt;&#x2F;code&gt;, &lt;code&gt;cargo&lt;&#x2F;code&gt;, and friends. Use rustup if you don&#x27;t have &lt;code&gt;rust-lldb&lt;&#x2F;code&gt;; then make sure that $HOME&#x2F;.cargo&#x2F;bin is in the &lt;code&gt;PATH&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You should see output like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(lldb) command source -s 0 &amp;#39;&#x2F;tmp&#x2F;rust-lldb-commands.6F5RYs&amp;#39;
Executing commands in &amp;#39;&#x2F;tmp&#x2F;rust-lldb-commands.6F5RYs&amp;#39;.
(lldb) command script import &amp;quot;&#x2F;Users&#x2F;bryce&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;etc&#x2F;lldb_rust_formatters.py&amp;quot;
(lldb) type summary add --no-value --python-function lldb_rust_formatters.print_val -x &amp;quot;.*&amp;quot; --category Rust
(lldb) type category enable Rust
(lldb) target create &amp;quot;target&#x2F;debug&#x2F;deps&#x2F;rcplayer-8b3e6af7e4113faf&amp;quot;
Current executable set to &amp;#39;target&#x2F;debug&#x2F;deps&#x2F;rcplayer-8b3e6af7e4113faf&amp;#39; (x86_64).
(lldb)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If instead you see lots of warnings about missing symbols, instead go back and re-read this &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;cargo&#x2F;issues&#x2F;4056#issue-228928448&quot;&gt;cargo issue&lt;&#x2F;a&gt;. TL;DR -- use the executable in &lt;strong&gt;target&#x2F;debug&#x2F;deps&#x2F;&lt;&#x2F;strong&gt; which ends in a git hash. Otherwise, at the time of writing this, its not guarranteed that LLDB will work.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;exiting-an-lldb-session&quot;&gt;Exiting an LLDB session&lt;&#x2F;h3&gt;
&lt;p&gt;If LLDB is even close to working, the prompt will change to &amp;quot;lldb&amp;quot; as we saw in the expected output sample above. We&#x27;re now inside a LLDB repl, and any time LLDB pauses or the exexecutable we&#x27;re running finishes, we&#x27;ll be dropped back into this repl.&lt;&#x2F;p&gt;
&lt;p&gt;To get out of the repl, we have a couple of options:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;CTRL-D - works basically anywhere: ssh, bash, and most interactive command line tools&lt;&#x2F;li&gt;
&lt;li&gt;q - this works in LLDB, GDB (and also &lt;code&gt;less&lt;&#x2F;code&gt;). Using q will cause LLDB to confirm with me that I &lt;em&gt;really really&lt;&#x2F;em&gt; want to quit&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;2-load-program-with-args&quot;&gt;2 - Load program with args&lt;&#x2F;h2&gt;
&lt;p&gt;Okay, so we know how to enter and exit a LLDB session with our Rust program at this point. Lots of the time, I want to feed arguments to my program while its being debugged or else nothing interesting will happen. LLDB makes this super simple. We just put our commands at the end of the rust-lldb invocation. Example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ sudo rust-lldb target&#x2F;debug&#x2F;deps&#x2F;mainMYGITHASH arg1 arg2
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If that worked you should see a line in the terminal like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(lldb) settings set -- target.run-args  &amp;quot;arg1&amp;quot; &amp;quot;arg2
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;change-arguments-between-runs-of-your-rust-program&quot;&gt;Change arguments between runs of your rust program&lt;&#x2F;h3&gt;
&lt;p&gt;At the &lt;code&gt;(lldb)&lt;&#x2F;code&gt; prompt type this to change the argument to &amp;quot;foobar&amp;quot;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;settings set -- target.run-args &amp;quot;arg1&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;3-debug-your-rust-program&quot;&gt;3 - Debug your rust program&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;set-a-breakpoint&quot;&gt;Set a breakpoint&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;b&lt;&#x2F;code&gt; command sets a new breakpoint taking the name of a function (or a regular expression) as an argument.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(lldb) b my_fn_name
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If it worked, you&#x27;ll see something like:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;Breakpoint 1: where = rcplayer`rcplayer::parse_element + 21 at main.rs:83, address = 0x00000001000113f5
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;See &lt;code&gt;help b&lt;&#x2F;code&gt; for more details on specifying setting breakpoints in files at line numbers. I haven&#x27;t quite figured this out yet in multi-file projects.&lt;&#x2F;p&gt;
&lt;p&gt;To see the current list of breakpoints use &lt;code&gt;breakpoint list&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;run-the-program&quot;&gt;Run the program&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;r&lt;&#x2F;code&gt; command (short for &lt;code&gt;run&lt;&#x2F;code&gt;). Example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(lldb) r
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The rust executable will run as a new process until lldb either (1) hits a breakpoint, at which point it will pause, OR (2) the process terminates. In either case, LLDB will present the repl.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;see-variables-in-the-current-stack-frame&quot;&gt;See variables in the current stack frame&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;frame variable&lt;&#x2F;code&gt; command prints out all the variables in the current stack. Example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(lldb) frame variable
(mkv::elements::Element *) element = &amp;amp;0x100617190
(mkv::elements::Element *) element = &amp;amp;0x0
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;print-out-a-variable-or-expression&quot;&gt;Print out a variable or expression&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;p&lt;&#x2F;code&gt; command prints out a variable (and sometimes expressions, though some trial error may be required). Example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(lldb) p element
(mkv::elements::Element *) $0 = &amp;amp;0x0
(lldb) p *element
error: Couldn&amp;#39;t apply expression side effects : Couldn&amp;#39;t dematerialize a result variable: couldn&amp;#39;t read its memory
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;see-some-context-from-the-source-code-of-the-program-where-execution-is-paused&quot;&gt;See some context from the source code of the program where execution is paused&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;list&lt;&#x2F;code&gt; command will show where are you now. If there&#x27;s no process running, it seems to print out part of &lt;code&gt;fn main(){}&lt;&#x2F;code&gt;&#x27;s code for me. Example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(lldb) list
   87               content: Binary(ref bytes),
   88           } =&amp;gt; {
   89               let raw_id = four_u8_to_u64(bytes.as_slice());
   90               let class = id_to_class(raw_id);
   91               format!(&amp;quot;SeekID ({:?})&amp;quot;, &amp;amp;class)
   92           }
   93           Element {
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;To get ready to debug, make sure you have a &lt;em&gt;debug&lt;&#x2F;em&gt; build available. Run rust-lldb with sudo on the rust executable inside target&#x2F;debug&#x2F;deps&#x2F;. The most important commands to debug a program are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;b&lt;&#x2F;code&gt; - set breakpoint(s) before running the program&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;r&lt;&#x2F;code&gt; - start a new process&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;list&lt;&#x2F;code&gt; - shows the code where we paused&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;frame variable&lt;&#x2F;code&gt; - shows variables current state in the current frame&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;p&lt;&#x2F;code&gt; print an expression&lt;&#x2F;li&gt;
&lt;li&gt;CTRL-D - quits GDB immediately&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Let me know about other tips you have on using LLDB in general or with Rust in particular!&lt;&#x2F;p&gt;
&lt;p&gt;Join the &lt;a href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;comments&#x2F;7pi6e1&#x2F;debugging_rust_programs_with_lldb_on_macos&#x2F;&quot;&gt;conversation on Reddit&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Recurse Day 3: Port simple_decoder.c to Rust</title>
		<published>2018-01-10T00:00:00+00:00</published>
		<updated>2018-01-10T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/recurse-day-3-post-simple-decoder-c-to-rust/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/recurse-day-3-post-simple-decoder-c-to-rust/</id>
		<content type="html">&lt;p&gt;I want to learn more about how video codecs work so someday I can grow up to be a Jedi-master Video Engineer. To aid me on this epic quest, I&#x27;m doing the one week &amp;quot;mini-batch&amp;quot; at the &lt;a href=&quot;https:&#x2F;&#x2F;www.recurse.com&#x2F;&quot;&gt;Recurse Center&lt;&#x2F;a&gt;. Here&#x27;s my notes from Day 3.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lesiurely-morning&quot;&gt;Lesiurely morning&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m getting really tired from all the late nights, so I more or less took the morning to start some laundry (ut-oh...need to shuffle that a few hours ag...) and write a checkin on the Recurse Center chat program describing what I&#x27;ve done and plan to do today.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;blogging-about-lldb-and-rust&quot;&gt;Blogging about LLDB and Rust&lt;&#x2F;h2&gt;
&lt;p&gt;Then I immediately started doing other things! Specifically, writing this blog post about &lt;a href=&quot;&#x2F;blog&#x2F;debugging-rust-programs-with-lldb&#x2F;&quot;&gt;debugging Rust with LLDB on a MacOS&lt;&#x2F;a&gt;, but I think that was a great distillation of hard to find information into a succint and approachable format. I&#x27;ll call that a success overall...just not my actual goal.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;learning-how-to-write-a-lisp&quot;&gt;Learning how to write a lisp&lt;&#x2F;h2&gt;
&lt;p&gt;Over falalel lunch, I had the best conversation about how one implements user-defined functions in a homemade lisp interpreter and then how to invoke that function later. Apparently, this requires a &amp;quot;special form&amp;quot; in the AST whenever the parser encounters the function keyword. This special form captures the function&#x27;s:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;name&lt;&#x2F;li&gt;
&lt;li&gt;arguments&lt;&#x2F;li&gt;
&lt;li&gt;body as an AST&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The whole thing is stored inside some kind of symbol table so that we can invoke it later.&lt;&#x2F;p&gt;
&lt;p&gt;Okay, so we&#x27;ve parsed out this funciton into a usable form. Great! Now, let&#x27;s evaluate our AST and handle a function invocation. (1) Lookup the function by name in our symbol table. (2) Bind the proper tokens to the function arguments. (3) Evaluate the function body&#x27;s AST as normal. I &lt;em&gt;think&lt;&#x2F;em&gt; that mostly makes sense to me, though it&#x27;d take me a while to get that working.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;port-simple-decoder-c-to-rust&quot;&gt;Port simple_decoder.c to Rust&lt;&#x2F;h2&gt;
&lt;p&gt;Okay, so I have &lt;a href=&quot;https:&#x2F;&#x2F;chromium.googlesource.com&#x2F;webm&#x2F;libvpx&#x2F;+&#x2F;master&#x2F;examples&#x2F;simple_decoder.c&quot;&gt;this sample C program&lt;&#x2F;a&gt; doing exactly what I want (almost), and there&#x27;s a &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;libvpx-native-sys&quot;&gt;generated crate for libvpx&lt;&#x2F;a&gt;, so I&#x27;m going  projectto start by trying to compile the crate on my system...&lt;&#x2F;p&gt;
&lt;h3 id=&quot;make-pkg-config-happy&quot;&gt;Make pkg-config happy&lt;&#x2F;h3&gt;
&lt;p&gt;No love, apparently I need to have a file name &lt;em&gt;vpx.pc&lt;&#x2F;em&gt; (&amp;quot;pc&amp;quot; means &lt;em&gt;package config maybe?&lt;&#x2F;em&gt;) to satisfy the needs of pkg-config. Its telling me to put that file into my PKG_CONFIG_PATH environment variable. Okay, let&#x27;s go see if there&#x27;s a vpx.pc somewhere in my libvpx build directory.... (Note to self: someday actually learn how to use &lt;code&gt;find&lt;&#x2F;code&gt;. For now using &lt;a href=&quot;https:&#x2F;&#x2F;the.exa.website&#x2F;&quot;&gt;&lt;code&gt;exa --tree | grep .pc&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to search recursively through the build dir. It&#x27;s in the top level of build. Anyway, I found it -- time to look at PKG_CONFIG_PATH... &lt;code&gt;env | grep PKG&lt;&#x2F;code&gt; gives me nothing, so...maybe I try &lt;code&gt;export PKG_CONFIG_PATH=[my build dir]&lt;&#x2F;code&gt;. Hooray!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;make-rust-type-checker-happy&quot;&gt;Make Rust type checker happy&lt;&#x2F;h3&gt;
&lt;p&gt;That got to a whole new set of errors. All of these errors are about mismatched types:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ cargo build
   Compiling libvpx-native-sys v4.0.2
   Compiling vp9_simple_decoder v0.1.0
error[E0308]: mismatched types
  --&amp;gt; src&#x2F;main.rs:22:13
   |
22 |             ffi::VPX_CODEC_MEM_ERROR =&amp;gt; Error::Mem,
   |             ^^^^^^^^^^^^^^^^^^^^^^^^ expected u32, found enum `ffi::vpx_codec_err_t`
   |
   = note: expected type `u32`
              found type `ffi::vpx_codec_err_t`
....
error: aborting due to 8 previous errors

error: Could not compile `vp9_simple_decoder`.

To learn more, run the command again with --verbose.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Hmm, literally all of the errors are about &lt;code&gt;u32&lt;&#x2F;code&gt; being expected by the function signature, but instead my code is giving back &lt;code&gt;ffi::vpx_codec_err_t&lt;&#x2F;code&gt; typed data. The definition of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;DiamondLovesYou&#x2F;rust-vpx&#x2F;blob&#x2F;master&#x2F;src&#x2F;sys&#x2F;lib.rs#L174&quot;&gt;&lt;code&gt;vpx_codec_err_t&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;pub type vpx_codec_err_t = Enum_Unnamed3;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;...and the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;DiamondLovesYou&#x2F;rust-vpx&#x2F;blob&#x2F;master&#x2F;src&#x2F;sys&#x2F;lib.rs#L163&quot;&gt;definition of &lt;code&gt;Enum_Unnamed3&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; (&lt;em&gt;love&lt;&#x2F;em&gt; that name by the way) is...&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;pub type Enum_Unnamed3 = ::libc::c_uint;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;...and the &lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;libc&#x2F;0.2.71&#x2F;x86_64-apple-darwin&#x2F;libc&#x2F;type.c_uint.html&quot;&gt;definition of &lt;code&gt;c_uint&lt;&#x2F;code&gt; from libc&lt;&#x2F;a&gt; is...on my platform...&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;type c_uint = u32;

&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Not sure what I was expecting, but I assumed it would in fact be the type &lt;code&gt;u32&lt;&#x2F;code&gt; the compiler seems to want. So, &lt;em&gt;perhaps&lt;&#x2F;em&gt; I could try to cast the type in my crate and keep going, OR I could try to understand why this would happen. First, I&#x27;ll try to cast to just get this working and maybe later come back to learning why this type alias isn&#x27;t working the way it maybe should. Wait wait wait -- I don&#x27;t need to cast, I&#x27;ll change the code I &amp;quot;borrowed&amp;quot; from &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;DiamondLovesYou&#x2F;rust-vpx&#x2F;blob&#x2F;master&#x2F;src&#x2F;lib&#x2F;lib.rs&quot;&gt;the rustvpx companion crate&lt;&#x2F;a&gt; to expect &lt;code&gt;ffi::vpx_codec_err_t&lt;&#x2F;code&gt; types instead of &lt;code&gt;u32&lt;&#x2F;code&gt; since I know that these should always be the same on my platform. (What could go wrong?!). That worked!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;make-ld-happy&quot;&gt;Make ld happy&lt;&#x2F;h3&gt;
&lt;p&gt;New errors now:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ cargo build
   Compiling vp9_simple_decoder v0.1.0
error: linking with `cc` failed: exit code: 1
  |
  = note: &amp;quot;cc&amp;quot; &amp;quot;-m64&amp;quot; &amp;quot;-L&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps&#x2F;vp9_simple_decoder-761a7810146c7575.vp9_simple_decoder0.rust-cgu.o&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps&#x2F;vp9_simple_decoder-761a7810146c7575.vp9_simple_decoder1.rust-cgu.o&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps&#x2F;vp9_simple_decoder-761a7810146c7575.vp9_simple_decoder2.rust-cgu.o&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps&#x2F;vp9_simple_decoder-761a7810146c7575.vp9_simple_decoder3.rust-cgu.o&amp;quot; &amp;quot;-o&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps&#x2F;vp9_simple_decoder-761a7810146c7575&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps&#x2F;vp9_simple_decoder-761a7810146c7575.crate.allocator.rust-cgu.o&amp;quot; &amp;quot;-Wl,-dead_strip&amp;quot; &amp;quot;-nodefaultlibs&amp;quot; &amp;quot;-L&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps&amp;quot; &amp;quot;-L&amp;quot; &amp;quot;&#x2F;usr&#x2F;local&#x2F;lib&amp;quot; &amp;quot;-L&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps&#x2F;libvpx_sys-3b31031fb7744e6c.rlib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps&#x2F;liblibc-731e9e00d9493fb1.rlib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&#x2F;libstd-3bdc66d380ab3ae0.rlib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&#x2F;liballoc_jemalloc-7d37db020b297d4d.rlib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&#x2F;liballoc_system-6020283742edae37.rlib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&#x2F;librand-a9c96b55be5fbf12.rlib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&#x2F;libpanic_unwind-cea338bc19530d4f.rlib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&#x2F;libunwind-ec9c6c2125b5c0ae.rlib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&#x2F;liblibc-b32efaa792018eef.rlib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&#x2F;liballoc-010885f4927e31a8.rlib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&#x2F;libstd_unicode-d8d07f9be800bfd6.rlib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&#x2F;libcore-f4176b2f23d80db2.rlib&amp;quot; &amp;quot;&#x2F;home&#x2F;user&#x2F;.rustup&#x2F;toolchains&#x2F;stable-x86_64-apple-darwin&#x2F;lib&#x2F;rustlib&#x2F;x86_64-apple-darwin&#x2F;lib&#x2F;libcompiler_builtins-6e4af26e08893557.rlib&amp;quot; &amp;quot;-l&amp;quot; &amp;quot;vpx&amp;quot; &amp;quot;-l&amp;quot; &amp;quot;m&amp;quot; &amp;quot;-l&amp;quot; &amp;quot;System&amp;quot; &amp;quot;-l&amp;quot; &amp;quot;resolv&amp;quot; &amp;quot;-l&amp;quot; &amp;quot;pthread&amp;quot; &amp;quot;-l&amp;quot; &amp;quot;c&amp;quot; &amp;quot;-l&amp;quot; &amp;quot;m&amp;quot;
  = note: ld: library not found for -lvpx
          clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: aborting due to previous error
error: Could not compile `vp9_simple_decoder`.
To learn more, run the command again with --verbose.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Looks like the system linker &lt;code&gt;ld&lt;&#x2F;code&gt; can&#x27;t find the library vpx (&amp;quot;ld: library not found for -lvpx&amp;quot;). That&#x27;s probably a think I can duckduckgo.... The first few results are not super helpful. Okay, let&#x27;s actually try to grok that command cargo spat out:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cc&lt;&#x2F;code&gt; - I know this is a c compiler (no idea how I know this), probably an apple version of gcc..yep.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-L &amp;quot;&#x2F;path&#x2F;to&#x2F;rust&#x2F;library&amp;quot;&lt;&#x2F;code&gt; - looks like we&#x27;re telling &lt;code&gt;cc&lt;&#x2F;code&gt; about other libraries to link against by providing the full path. That is most of the monstruously large error message above.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-l vpx&lt;&#x2F;code&gt; - I&#x27;d venture a guess this is a library but its one that the linker or compiler is supposed to just know where it is.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Okay, lets figure out how &lt;code&gt;cc&lt;&#x2F;code&gt; uses the &lt;code&gt;-l&lt;&#x2F;code&gt; flag for sure...nope, there&#x27;s no entries for this in &lt;code&gt;man cc&lt;&#x2F;code&gt;. Apparently, this is undocumented feature. Okay, well cargo error says something about &lt;code&gt;ld&lt;&#x2F;code&gt; so lets try &lt;code&gt;man ld&lt;&#x2F;code&gt; and looking for something about where it looks for libraries (my hypothesis for what &lt;code&gt;-l vpx&lt;&#x2F;code&gt; means). Sure ensure, there&#x27;s a description of how this works:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ man ld
...

   Search paths
     ld maintains a list of directories to search for a library or framework to use.  The default library search path is &#x2F;usr&#x2F;lib then &#x2F;usr&#x2F;local&#x2F;lib.  The -L
     option will add a new library search path.  The default framework search path is &#x2F;Library&#x2F;Frameworks then &#x2F;System&#x2F;Library&#x2F;Frameworks.  (Note: previously,
     &#x2F;Network&#x2F;Library&#x2F;Frameworks was at the end of the default path.  If you need that functionality, you need to explicitly add -F&#x2F;Network&#x2F;Library&#x2F;Frameworks).
     The -F option will add a new framework search path.  The -Z option will remove the standard search paths.  The -syslibroot option will prepend a prefix to all
     search paths.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So, if I know which lib is the library file for vpx, I can stick that file in &#x2F;usr&#x2F;lib or one of the other directories mentioned. Okay, wow I hate not knowing how C works at all. Phew...the rust libraries mostly have an &lt;em&gt;rlib&lt;&#x2F;em&gt; extension. Maybe there&#x27;s an rlib in the libvpx&#x2F;build directory? No, not even one file. Okay, maybe &amp;quot;dylib&amp;quot;? Nope. What about just things that have &amp;quot;lib&amp;quot;? Yep -- there&#x27;s a bunch .a files, like &lt;em&gt;libvpx.a&lt;&#x2F;em&gt;. Hmm, that sounds plausible. What does .a even mean? I know .o is for object files produced during a C compilation, whatever those are... Okay, well some guy named Mike has this on his &lt;a href=&quot;http:&#x2F;&#x2F;courses.cms.caltech.edu&#x2F;cs11&#x2F;material&#x2F;c&#x2F;mike&#x2F;misc&#x2F;compiling_c.html&quot;&gt;blog about .a files&lt;&#x2F;a&gt; and compiling C:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are other kinds of files as well, notably libraries (&amp;quot;.a&amp;quot; files) and shared libraries (&amp;quot;.so&amp;quot; files), but you won&#x27;t normally need to deal with them directly.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Oh, yeah! I&#x27;ve totally worked with .so files on linux before. A file ending in &amp;quot;.a&amp;quot; on MacOS is like a &amp;quot;.dll&amp;quot; for Windows or a &amp;quot;.so&amp;quot; for Linux. TIL!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;not-pwning-my-own-system-directories&quot;&gt;Not pwning my own system directories&lt;&#x2F;h3&gt;
&lt;p&gt;Okay, let&#x27;s copy that libvpx.a into &#x2F;usr&#x2F;lib and see what happens...Uh, apparently thats not okay with Apple:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;cp: &#x2F;usr&#x2F;lib&#x2F;libvpx.a: Operation not permitted
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s a lot of symlinks in there, maybe I can just symlink libvpx.a? Nope, same result.&lt;&#x2F;p&gt;
&lt;p&gt;I really feel like I should be able to do this, so there&#x27;s something specific about that directory I don&#x27;t understand. Sure enough, there&#x27;s a great explanation of how &lt;a href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;32910619&#x2F;ln-usr-lib-libssl-dylib-operation-not-permitted-osx#32911368&quot;&gt;Apple has deliberately locked down that directory&lt;&#x2F;a&gt; to keep us all safe. There&#x27;s also an alternative here. Use the &lt;code&gt;DYLD_LIBRARY_PATH&lt;&#x2F;code&gt; environment variable instead. Let&#x27;s give that a whirl. No difference whatsoever. Same result we started with.&lt;&#x2F;p&gt;
&lt;p&gt;Hmm, maybe let&#x27;s try a different approach. Maybe I could get more info out of cargo by using the verbose flag. Let&#x27;s try:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ cargo build -v
       Fresh libc v0.2.35
       Fresh pkg-config v0.3.9
       Fresh semver-parser v0.7.0
       Fresh libvpx-native-sys v4.0.2
   Compiling vp9_simple_decoder v0.1.0 (file:&#x2F;&#x2F;&#x2F;Users&#x2F;bryce&#x2F;programming&#x2F;vp9_simple_decoder)
     Running `rustc ...`
error: linking with `cc` failed: exit code: 1
  |
  ...
  = note: ld: library not found for -lvpx
          clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: aborting due to previous error
error: Could not compile `vp9_simple_decoder`.
Caused by:
  process didn&amp;#39;t exit successfully: `rustc --crate-name vp9_simple_decoder src&#x2F;main.rs --crate-type bin --emit=dep-info,link -C debuginfo=2 -C metadata=761a7810146c7575 -C extra-filename=-761a7810146c7575 --out-dir &#x2F;Users&#x2F;bryce&#x2F;programming&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps -L dependency=&#x2F;Users&#x2F;bryce&#x2F;programming&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps --extern vpx_sys=&#x2F;Users&#x2F;bryce&#x2F;programming&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps&#x2F;libvpx_sys-3b31031fb7744e6c.rlib --extern libc=&#x2F;Users&#x2F;bryce&#x2F;programming&#x2F;vp9_simple_decoder&#x2F;target&#x2F;debug&#x2F;deps&#x2F;liblibc-731e9e00d9493fb1.rlib -L native=&#x2F;usr&#x2F;local&#x2F;lib` (exit code: 101)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Okay, now that &lt;code&gt;rustc&lt;&#x2F;code&gt; invocation at the bottom also has &lt;code&gt;-L native=&#x2F;usr&#x2F;local&#x2F;lib&lt;&#x2F;code&gt; in it....I can definitely put anything in that directory I want to. Let&#x27;s try it!&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ ln -s &#x2F;home&#x2F;user&#x2F;libvpx&#x2F;build&#x2F;libvpx.a &#x2F;usr&#x2F;local&#x2F;lib&#x2F;libvpx.a
$ cargo build
   Compiling vp9_simple_decoder v0.1.0 (file:&#x2F;&#x2F;&#x2F;Users&#x2F;bryce&#x2F;programming&#x2F;vp9_simple_decoder)
    Finished dev [unoptimized + debuginfo] target(s) in 0.66 secs
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Woohoo! I compiled the thing. Oh man that&#x27;s exciting. Time for pizza and human contact...I&#x27;ll see what Andy&#x27;s up to with his Rust lisp interpreter.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;porting-a-few-lines-of-simple-decoder&quot;&gt;Porting a few lines of simple_decoder&lt;&#x2F;h3&gt;
&lt;p&gt;Okay, I&#x27;ve tried to do a mechanical port of simple_decoder.c file directly using the low-level C bindings in &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;libvpx-native-sys&quot;&gt;crate libvpx-native-sys&lt;&#x2F;a&gt;. However, the first interesting thing that happens in simple_decoder, is that we open a file and try to parse it using &lt;code&gt;vpx_video_reader_open()&lt;&#x2F;code&gt;...which isn&#x27;t included in the crate I&#x27;m using.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s some &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;DiamondLovesYou&#x2F;rust-vpx&#x2F;blob&#x2F;master&#x2F;src&#x2F;sys&#x2F;regen-ffi.sh&quot;&gt;mention of bindgen&lt;&#x2F;a&gt; in some of `the code, so I go and look at the infamous &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;bindgen&quot;&gt;bindgen crate&lt;&#x2F;a&gt; and its very accessible &lt;a href=&quot;https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rust-bindgen&#x2F;introduction.html&quot;&gt;tutorial&lt;&#x2F;a&gt;. Step 1 in the tutorial is:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Add bindgen as a Build Dependency&lt;&#x2F;p&gt;
&lt;p&gt;Declare a build-time dependency on bindgen by adding it to the [build-dependencies] section of our crate&#x27;s Cargo.toml metadata file:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[build-dependencies]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;bindgen = &amp;quot;0.26.3&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;blockquote&gt;
&lt;p&gt;...Uh oh, there&#x27;s no &lt;code&gt;bindgen&lt;&#x2F;code&gt; dependency inside &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;DiamondLovesYou&#x2F;rust-vpx&#x2F;blob&#x2F;master&#x2F;src&#x2F;sys&#x2F;Cargo.toml&quot;&gt;Cargo.toml&lt;&#x2F;a&gt; in libvpx_sys crate...instead there&#x27;s a PNaCl helper create. What the flip is PNaCl and why does it sound so familiar? A few duckduckgo&#x27;s later, I find the &lt;a href=&quot;https:&#x2F;&#x2F;developer.chrome.com&#x2F;native-client&#x2F;nacl-and-pnacl&quot;&gt;project description&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;h3 id=&quot;native-client-nacl&quot;&gt;Native Client (NaCl)&lt;&#x2F;h3&gt;
&lt;p&gt;Native Client enables the execution of native code securely inside web applications through the use of advanced Software Fault Isolation (SFI) techniques. Native Client allows you to harness a client machine’s computational power to a fuller extent than traditional web technologies. It does this by running compiled C and C++ code at near-native speeds, and exposing a CPU’s full capabilities, including SIMD vectors and multiple-core processing with shared memory.&lt;&#x2F;p&gt;
&lt;p&gt;While Native Client provides operating system independence, it requires you to generate architecture-specific executables (nexe) for each hardware platform. This is neither portable nor convenient, making it ill-suited for the open web.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;portable-native-client-pnacl&quot;&gt;Portable Native Client (PNaCl)&lt;&#x2F;h2&gt;
&lt;p&gt;PNaCl solves the portability problem by splitting the compilation process into two parts:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;compiling the source code to a bitcode executable (pexe), and&lt;&#x2F;li&gt;
&lt;li&gt;translating the bitcode to a host-specific executable as soon as the module loads in the browser but before any code execution.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Right, so like WebAssembly but that only works on Chrome and friends. Oh, and then I see the &lt;a href=&quot;https:&#x2F;&#x2F;developer.chrome.com&#x2F;native-client&#x2F;migration&quot;&gt;WebAssembly migration guide&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given the momentum of cross-browser WebAssembly support, we plan to focus our native code efforts on WebAssembly going forward and plan to remove support for PNaCl in Q1 2018 (except for Chrome Apps).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Okay, why is this crate using PNaCl? I&#x27;m so confused. At any rate, I have to stop for the day and go to sleep.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;open-questions&quot;&gt;Open Questions&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Should I contribute patches on top of rustvpx crate to get the bits I need to decode? Or should I rewrite the example C program to &lt;em&gt;only&lt;&#x2F;em&gt; use functions that are available in libvpx-sys crate?&lt;&#x2F;li&gt;
&lt;li&gt;Do any of the functions inside libvpx actually work at all?&lt;&#x2F;li&gt;
&lt;li&gt;Could I get Doxygen to produce the documentation for libvpx from the C code? Or is there a copy of the docs floating around on the interwebs?&lt;&#x2F;li&gt;
&lt;li&gt;Should I go back and just work on nom &#x2F; mkv.rs? I might ping that maintainer again and call it a night. We&#x27;ll see what he says in the morning&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Recurse Center Day 2: Seeks and Blocks</title>
		<published>2018-01-09T00:00:00+00:00</published>
		<updated>2018-01-09T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/recurse-center-day-2-seeks-and-blocks/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/recurse-center-day-2-seeks-and-blocks/</id>
		<content type="html">&lt;p&gt;I want to learn more about how codecs work so that I one day I can grow up to be a Jedi-master Video Engineer. Here&#x27;s my notes from Day 2.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-0-actually-reading-the-specs&quot;&gt;Step 0 - Actually Reading the Specs&lt;&#x2F;h2&gt;
&lt;p&gt;The Matroska specs helpfully include a &lt;a href=&quot;https:&#x2F;&#x2F;www.matroska.org&#x2F;technical&#x2F;diagram&#x2F;index.html&quot;&gt;diagram&lt;&#x2F;a&gt; which I&#x27;ve ignored up until now which answers about 95% of my questions from last night. To quote the specs:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Track section has basic information about each of the tracks. For instance, is it a video, audio or subtitle track? What resolution is the video? What sample rate is the audio? The Track section also says what codec to use to view the track, and has the codec&#x27;s private data for the track.&lt;&#x2F;p&gt;
&lt;p&gt;The Clusters section has all of the Clusters. These contain all of the video frames and audio for each track.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;There&#x27;s a lot more I learned today, but I&#x27;m going to focus on doing it and possibly write down interesting things later.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Recurse Center Day 1: VP9 != Matroska</title>
		<published>2018-01-08T00:00:00+00:00</published>
		<updated>2018-01-08T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/recurse-center-day-1-vp9-webm-matroska/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/recurse-center-day-1-vp9-webm-matroska/</id>
		<content type="html">&lt;p&gt;I want to learn more about how video codecs work so someday I can grow up to be a Jedi-master Video Engineer. To aid me on this epic quest, I&#x27;m doing the one week &amp;quot;mini-batch&amp;quot; at the &lt;a href=&quot;https:&#x2F;&#x2F;www.recurse.com&#x2F;&quot;&gt;Recurse Center&lt;&#x2F;a&gt;. Here&#x27;s my notes from Day 1.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;step-1-find-a-vp9-sample-video&quot;&gt;Step 1 - Find a VP9 sample video&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2018&#x2F;tears-of-steel-awesome-in-space.png&quot; alt=&quot;I just want to be awesome in space&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=R6MlUcmOul8&quot;&gt;Tears of Steel&lt;&#x2F;a&gt; is legendary for all the different kinds of lighting, motion, color, and uh...&lt;em&gt;things&lt;&#x2F;em&gt; one might need to compress with a video encoder. I&#x27;ve been wanting to play with that video for a long time. It&#x27;s licensed as creative commons and made by the good people at Blender, and its actually pretty fun to watch. A quick search on duckduckgo shows you can download a webm version of the file. This is &lt;em&gt;great news&lt;&#x2F;em&gt; because webm videos use the vp9 codec, which is what I&#x27;m trying to use my weeklong training program at the Recurse Center in NYC to learn about.&lt;&#x2F;p&gt;
&lt;p&gt;Except that webm doesn&#x27;t only mean vp9...it also means vp8. Time to transcode the video using ffmpeg to use vp9:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ffmpeg -i tears_of_steel_1080p.webm -c:v libvpx-vp9 -c:a libopus output.webm
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-i&lt;&#x2F;code&gt; means input file&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-c:v&lt;&#x2F;code&gt; specifies the video codec for all streams in the file, and &lt;code&gt;lipvpx-vp9&lt;&#x2F;code&gt; means use the libvpx library&#x27;s vp9 codec&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-c:a&lt;&#x2F;code&gt; is the same thing, but for audio&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;output.webm&lt;&#x2F;code&gt; is the output file that ffmpeg will create. It infers lots of things from the fact I&#x27;ve used the .webm file extension&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Oh man...ffmpeg is going so slow. The output looks like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;frame= 7540 fps=3.6 q=0.0 size=   13211kB time=00:05:14.31 bitrate= 344.3kbits&#x2F;s speed=0.15x
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The video is 12 minutes and 14 seconds long, but I&#x27;ve already waited for at least an hour...&lt;&#x2F;p&gt;
&lt;h2 id=&quot;two-hours-later&quot;&gt;Two hours later...&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve retranscoded to vp9! I&#x27;ve managed to fix three bugs in my blog while waiting for the transcode to finish. If I had to guess, the job took so long because I didn&#x27;t specify any quantization parameters (that&#x27;s the &amp;quot;q=0.0&amp;quot;)...I really need to read up on exactly what quanta are on &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Quantization_(signal_processing)&quot;&gt;wikipedia&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-quick-digression-into-quantization-with-a-picture&quot;&gt;A quick digression into quantization...with a picture!&lt;&#x2F;h3&gt;
&lt;p&gt;The intro to that article was really helpful. Apparently, quantization basically just means taking some information that has an infinite range of possible values and chopping it into a finite number of discrete values. Actually my picture used in the header of this blog is a great example of quantization. The original photo on the left has tons and tons of colors, but I&#x27;ve forced the header image (on the right) to use fewer than 10 colors. Quantization. Not so scary a concept from 20,000 feet.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2018&#x2F;quantize-my-face.jpg&quot; alt=&quot;Quantize my face&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Using &lt;a href=&quot;https:&#x2F;&#x2F;mkvtoolnix.download&#x2F;downloads.html#macosx&quot;&gt;&lt;code&gt;mkvinfo&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, I can see that my output.webm is actually VP9 now. Phew! Okay, I have a sample video.&lt;&#x2F;p&gt;
&lt;p&gt;Before coming to the Recurse Center, I&#x27;d been reading up on the &lt;a href=&quot;https:&#x2F;&#x2F;www.matroska.org&#x2F;technical&#x2F;specs&#x2F;index.html&quot;&gt;Matroska format&lt;&#x2F;a&gt; to get ready to build my decoder...but total fail, that&#x27;s the just the &lt;strong&gt;container&lt;&#x2F;strong&gt; format and contains 0% of video information. Now the awesome rust libraries I found are basically worthless...Hmm, time to rethink the project? Nope, time for boba tea and a little reflection.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;boba-interlude&quot;&gt;Boba Interlude&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s raining and Boba was delicious. I&#x27;m just starting to skim the table of contents on the &lt;a href=&quot;https:&#x2F;&#x2F;storage.googleapis.com&#x2F;downloads.webmproject.org&#x2F;docs&#x2F;vp9&#x2F;vp9-bitstream-specification-v0.6-20160331-draft.pdf&quot;&gt;VP9 spec&lt;&#x2F;a&gt;, and it&#x27;s a little overwhelming. I&#x27;ve asked for help for the most excellent &lt;a href=&quot;https:&#x2F;&#x2F;video-dev.slack.com&#x2F;&quot;&gt;video-dev slack org&lt;&#x2F;a&gt; about suitable alternative projects. Here&#x27;s some ideas that have come to mind:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Rewrite a simplified ffmpeg-like-tool that consumes libav or libvpx to transcode or decode a video&lt;&#x2F;li&gt;
&lt;li&gt;Just read through the whole VP9 spec&lt;&#x2F;li&gt;
&lt;li&gt;Contribute to FFMPEG&#x2F;VP9&#x2F;AV1 open source projects&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Might be helpful to take a step back here. When I leave Recurse Center, I want to understand more about how to efficiently encode and troubleshoot videos especially those using VP9. So, there&#x27;s actually a lot of value in just understanding the webm &#x2F; Matroska format...There&#x27;s also value in understanding how a video gets encoded using VP9 to understand the quality&#x2F;filesize tradeoffs and what encoding parameters can be tuned a bit better. Actually knowing the super deep technical areas of VP9 might not be totally necessary...&lt;&#x2F;p&gt;
&lt;p&gt;I just got a great suggestion from Slack -- try to contribute to the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;xiph&#x2F;aomanalyzer&quot;&gt;AOM Analyzer&lt;&#x2F;a&gt; project. I remember seeing this before -- and its really cool. However, it&#x27;s really broken on Firefox (my browser of choice since 2014) and Chrome. Maybe another time I&#x27;d look at that project again, but not today.&lt;&#x2F;p&gt;
&lt;p&gt;Contributing to ffmpeg or AV1 would be super amazing, but I think I need a stronger foundation in how the codecs themselves work. FFMPEG is notoriously hard to contribute to, but I wonder if there are beginner issues for AV1...ah, nope there&#x27;s only an &lt;a href=&quot;https:&#x2F;&#x2F;bugs.chromium.org&#x2F;p&#x2F;aomedia&#x2F;issues&#x2F;list&quot;&gt;issue tracker&lt;&#x2F;a&gt; for AV1 and no obvious on-ramp there, although there is documentation about building and testing the project. Still seems a little too pie-in-sky.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;decision-time&quot;&gt;Decision Time&lt;&#x2F;h3&gt;
&lt;p&gt;Why don&#x27;t I use the Matroska rust library to extract the VP9 bits of a video and try decoding the first frame first using libvpx; once I&#x27;ve got that working, I&#x27;ll try to read the spec to do that myself...Great! This could keep me busy for at least the whole week, but I&#x27;d likely learn a lot about how the codec works and build a thing that does something with a quick win and then lets me go deeper.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;matroska&quot;&gt;Matroska crate&lt;&#x2F;a&gt; I&#x27;ve been playing with doesn&#x27;t provide any access to the raw bytes, so there&#x27;s no way that will work. Maybe, I could just &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tuffy&#x2F;matroska&#x2F;issues&#x2F;1&quot;&gt;contribute to that project&lt;&#x2F;a&gt; a way to expose the lower level parsing logic from that crate. Wait...I should really look at that other library &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;mkv&quot;&gt;mkv&lt;&#x2F;a&gt;. Yeah! THis does &lt;em&gt;exactly&lt;&#x2F;em&gt; what I want! It spits out typed events corresponding to the start and end of each EBML element as it does a single pass through the file. I&#x27;ll build on top of that then!&lt;&#x2F;p&gt;
&lt;p&gt;Also, the Matroska stuff &lt;em&gt;does&lt;&#x2F;em&gt; actually seem to be super valuable. I really need to have a handle on the container format in order to find the image data for vp9 to process, and the webm project requires a lot of knowledge of the Matroska format...so it doesn&#x27;t look like a waste of time after all.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;playing-with-libvpx&quot;&gt;Playing with libvpx&lt;&#x2F;h2&gt;
&lt;p&gt;Okay, I&#x27;ll just start by making a new &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;bff&#x2F;vp9-player.rs&quot;&gt;GitLab project&lt;&#x2F;a&gt; and pushing the little bit of code I have now there...and I&#x27;ll add a README with a link to Tears of Steel source I&#x27;m working with (I would love to get a &amp;quot;Bars and Tones&amp;quot; sample video for easier sample...).&lt;&#x2F;p&gt;
&lt;p&gt;A bowl of Ramen and a karafe of sake later, it looks like the &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;mkv&quot;&gt;mkv crate&lt;&#x2F;a&gt; is exactly what I want for parsing Matroska packaging. It gives me the actual binary, the offset, and an enum variant for each Matroska ID. Amazing! So, now I need to see if I can interface this with libvpx...&lt;&#x2F;p&gt;
&lt;p&gt;9:45pm -- time to head back to my apartment.&lt;&#x2F;p&gt;
&lt;p&gt;Okay, I&#x27;ve compiled &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;webmproject&#x2F;libvpx&quot;&gt;lipvpx&lt;&#x2F;a&gt; by hand and looked closely at the most &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;webmproject&#x2F;libvpx&#x2F;blob&#x2F;master&#x2F;examples&#x2F;simple_decoder.c&quot;&gt;simple decoder example&lt;&#x2F;a&gt;. I&#x27;ve also quickly skimmed the &lt;a href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;vpx&quot;&gt;vpx crate&lt;&#x2F;a&gt; and it looks like all the necessary C functions have &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;DiamondLovesYou&#x2F;rust-vpx&#x2F;blob&#x2F;master&#x2F;src&#x2F;sys&#x2F;lib.rs&quot;&gt;rust bindings&lt;&#x2F;a&gt;. Unfortunately, none of my webm files work when I run them through the example program from libvpx. Running them through mkvalidator gives me errors on all of them, &lt;em&gt;but&lt;&#x2F;em&gt; VLC has no trouble with any of these files and ffprobe also is very happy with them.&lt;&#x2F;p&gt;
&lt;p&gt;Oops! Stupid user error on my part. Looks like that script only works on IVF files -- Today I Learned IVF means vp8. I swear I have never heard of IVF before today, and I assume it&#x27;s not something I&#x27;ll hear a lot about going forward. So, that script appears to have succeeded in creating a massive pile of images, but I&#x27;d love to verify. Time to download &lt;code&gt;ffplay&lt;&#x2F;code&gt; so I can follow the script that simple_decoder.c told me to run:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ffplay -f rawvideo -pix_fmt yuv420p -s 512x288 &#x2F;tmp&#x2F;out9-ivf
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Oh man...I have ffprobe and ffmpeg, but none of the other ff tools. I can never remember the write way to install ffmpeg and get ALL the tools, so here&#x27;s how:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Go to https:&#x2F;&#x2F;ffmpeg.zeranoe.com&#x2F;builds&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;Click the blue Download button&lt;&#x2F;li&gt;
&lt;li&gt;Copy everything from the bin&#x2F; directory of the unpacked tarball into &#x2F;usr&#x2F;local&#x2F;bin&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;Make sure it worked &lt;code&gt;which ffplay&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Let&#x27;s try that &lt;code&gt;ffplay&lt;&#x2F;code&gt; command again now... Success!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2018&#x2F;ffplay-tractor-from-raw.png&quot; alt=&quot;vp9 tractor video being played by ffplay by stitching together the raw yuv420p picture data&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;open-questions&quot;&gt;Open Questions&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;How &lt;em&gt;does&lt;&#x2F;em&gt; VP9 fit into webm? The code sample for simple_decoder.c makes it look like libvpx is actually parsing some matroska...but running mkvalidator seems to disprove that theory. No EBML found in the file.&lt;&#x2F;li&gt;
&lt;li&gt;What&#x27;s the difference between a &lt;code&gt;Seek&lt;&#x2F;code&gt; element and a &lt;code&gt;CuePoint&lt;&#x2F;code&gt;?&lt;&#x2F;li&gt;
&lt;li&gt;Where does encoded audio data actually go inside a Webm file? Inside &lt;code&gt;Blocks&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;SimpleBlocks&lt;&#x2F;code&gt;?&lt;&#x2F;li&gt;
&lt;li&gt;Why is timing so complex inside webm?&lt;&#x2F;li&gt;
&lt;li&gt;How can I feed raw bytes into libvpx for vp9? Hmm, actually I know a vp9 codec core contributor...I should ask him about this!&lt;&#x2F;li&gt;
&lt;li&gt;What can I reasonably try to accomplish tomorrow? I really think unpacking &lt;code&gt;SeekPositions&lt;&#x2F;code&gt; and possibly the &lt;code&gt;Video&lt;&#x2F;code&gt; element from the Webm file will point me to the correct places inside the file to find data to feed the vp9 codec. Maybe I&#x27;ll just try to wrap my head around those elements better.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Strategies for Returning References in Rust</title>
		<published>2017-03-04T00:00:00+00:00</published>
		<updated>2017-03-04T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/strategies-for-returning-references-in-rust/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/strategies-for-returning-references-in-rust/</id>
		<content type="html">&lt;p&gt;I&#x27;m going to demonstrate the situation where I most often have friction with The Borrow Checker and provide a few patterns I use to make my code compile.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s pretend we&#x27;re trying to connect to an imaginary Postgres database server using a connection pool. The API for this imaginary library requires us to first initialize a connection &lt;code&gt;Pool&lt;&#x2F;code&gt; with the connection string. Once the pool is initialized, we call &lt;code&gt;connect()&lt;&#x2F;code&gt; on it to get access to a usable, owned &lt;code&gt;Connection&lt;&#x2F;code&gt; for queries.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-returning-references-can-be-difficult&quot;&gt;Why Returning References Can Be Difficult&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s start with a naive approach that won&#x27;t even compile:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; This won&amp;#39;t compile!
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) -&amp;gt; &amp;amp;Connection {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; pool: Pool = postgres::connection_pool::new(url);
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; connection: Connection = pool.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();
    connection.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;set_database&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;startup.io&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;);
    &amp;amp;connection
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The compiler will complain about the lifetime of &lt;code&gt;&amp;amp;Connection&lt;&#x2F;code&gt; not lasting long enough. Why? The compiler wants to deallocate the &lt;code&gt;Connection&lt;&#x2F;code&gt; object at the end of the function, but it knows that the &lt;code&gt;&amp;amp;Connection&lt;&#x2F;code&gt; reference we&#x27;re trying to return points to that struct.&lt;&#x2F;p&gt;
&lt;p&gt;The Book does a great job of explaining &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;stable&#x2F;book&#x2F;ownership.html&quot;&gt;owned values&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;stable&#x2F;book&#x2F;references-and-borrowing.html&quot;&gt;borrowing&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;stable&#x2F;book&#x2F;lifetimes.html&quot;&gt;lifetimes&lt;&#x2F;a&gt;; but I&#x27;ll provide my own version here. I&#x27;m doing a lot of generalizing and glossing over complexities in the next few paragraphs. If you understand &lt;em&gt;why&lt;&#x2F;em&gt; my sample code won&#x27;t compile, feel free to skip ahead.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-digression-into-memory-allocation&quot;&gt;A Digression into Memory Allocation&lt;&#x2F;h3&gt;
&lt;p&gt;A computer has a fixed amount of physical RAM which we typically refer to as &amp;quot;memory.&amp;quot; As we write programs, all the data we process is represented in memory at some point during the execution of the program. We call the process of assigning memory to a specific running process &amp;quot;allocating&amp;quot; memory, and releasing ownership of memory &amp;quot;deallocating.&amp;quot; In the simplest case, the newly allocated memory stores the data we assign to the variable. For other kinds of data, we store the memory address of another variable. We call this second kind of variable a &amp;quot;pointer&amp;quot; because it &amp;quot;points&amp;quot; to other memory. (I&#x27;ll also use the term &amp;quot;reference&amp;quot; interchangeably with &amp;quot;pointer.&amp;quot;) Pointers exist mostly to (1) avoid duplicating data in memory and (2) provide a predictably sized variable which represents unpredictably sized data (such as user input or a data from a network call).&lt;&#x2F;p&gt;
&lt;h4 id=&quot;a-brief-digression-on-garbage-collectors&quot;&gt;A Brief Digression on Garbage Collectors&lt;&#x2F;h4&gt;
&lt;p&gt;Many languages including Java and Python use a &amp;quot;garbage collector&amp;quot; to decide when a process should free memory. Every so often, the garbage collector will (1) interrupt whatever the programmer&#x27;s code is doing, (2) hunt for memory that doesn&#x27;t have pointers to it, (3) and free that memory. Garbage collectors operate at &amp;quot;runtime,&amp;quot; while the program is running. This means that programmers can let the garbage collector figure out when to free memory automatically, which makes garbage collected languages easier and safer to write, but sometimes slower and with unpredictable memory usage.&lt;&#x2F;p&gt;
&lt;p&gt;Some languages don&#x27;t use a garbage collector. In C, the programmer must decide when to free up memory manually, which is difficult to do correctly. If the programmer tries to use a pointer after the memory has been freed, Bad Things can happen -- things like crashing the program or allowing a hacker to gain root access. What makes Rust unique is that although it doesn&#x27;t use a garbage collector, it still guarrantees that pointers will be safe to use.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;how-rust-achieves-memory-safety-without-a-garbage-collector&quot;&gt;How Rust Achieves Memory Safety without a Garbage Collector&lt;&#x2F;h4&gt;
&lt;p&gt;The Rust compiler uses the metaphor of home &amp;quot;ownership.&amp;quot; If memory is a house, then exactly one variable is the owner of that house and the death of that owner triggers an estate sale (aka deallocation). A variable owns memory when the programmer assigns data to it. When the owner goes out of scope and dies, that memory is deallocated. Because the compiler always knows when a variable goes out of scope, it always knows when to deallocate memory &lt;em&gt;at compile time&lt;&#x2F;em&gt; so Rust programs don&#x27;t need to pause and garbage collect while running.&lt;&#x2F;p&gt;
&lt;p&gt;As in real estate, ownership can be transferred. Like selling a house, assigning one variable to another variable transers ownership. We say that the value has been &amp;quot;moved.&amp;quot; Similar to how selling my home means moving my stuff, moves in Rust copy data to a new place in memory. This is called &amp;quot;move semantics.&amp;quot;&lt;&#x2F;p&gt;
&lt;p&gt;You might think that if a large amount of data is moved then we&#x27;re probably wasting RAM since the data must exist in at least two locations in memory. However, the Rust compiler heavily optimizes code when using the &lt;code&gt;--release&lt;&#x2F;code&gt; flag, so most of the time the compiler can see that we&#x27;re about to waste memory and simply reuse the existing memory location instead of stupidly copying.&lt;&#x2F;p&gt;
&lt;p&gt;Another problem introduced by move semantics is that you often want many variables to be able to access the same data from different places in your program. Ownership would seem to make that impossible since only one variable can own a memory location at a time. However, Rust also allows &amp;quot;borrowing&amp;quot; of memory. The programmer borrows memory by creating a pointer to it. If memory is a house and one variable is the owner, pointers are the renters who temporarily use that memory. If the owner goes out of scope and dies, pointers can no longer legally use that memory. The Rust compiler acts like a property management agency by tracking all ownership lifetimes and borrowing to make sure that no memory is being squatted on illegally. This aspect of the Rust compiler is known as The Borrow Checker, and it&#x27;s the thing which makes Rust uniquely Rust.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;back-to-the-code-sample&quot;&gt;Back to the code sample&lt;&#x2F;h3&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; This won&amp;#39;t compile!
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) -&amp;gt; &amp;amp;Connection {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; pool: Pool = postgres::connection_pool::new(url);
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; connection: Connection = pool.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();
    connection.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;set_database&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;startup.io&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;);
    &amp;amp;connection
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Going back to our example, there&#x27;s only one scope involved: the entire body of &lt;code&gt;fn connect() {..}&lt;&#x2F;code&gt;. So, at the end of &lt;code&gt;connect()&lt;&#x2F;code&gt;, all variables declared inside the function will be deallocated. Invoking &lt;code&gt;pool.connect()&lt;&#x2F;code&gt; creates an unnamed &lt;code&gt;Connection&lt;&#x2F;code&gt; object in this scope, but because that object is created inside of the &lt;code&gt;connect()&lt;&#x2F;code&gt; scope, it also must be deallocated at the end of function. This means that after we return the pointer to the &lt;code&gt;Connection&lt;&#x2F;code&gt;, there won&#x27;t be a &lt;code&gt;Connection&lt;&#x2F;code&gt; anymore! This is what the compiler is trying to tell us: that pointer will refer to freed memory after the end of the function.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;patterns-for-returning-references&quot;&gt;Patterns for Returning References&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;pattern-1-return-owned-values&quot;&gt;Pattern 1: Return Owned Values&lt;&#x2F;h3&gt;
&lt;p&gt;Give up on references, and just return a full copy of the value. Depending on the use case, this might be a good solution, and it&#x27;s often the easiest way to appease The Borrow Checker.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) -&amp;gt; Connection {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; pool: Pool = postgres::connection_pool::new(url);
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; connection: Connection = pool.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();
    connection.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;set_database&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;startup.io&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;);
    connection
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There are two changes needed in this trivial case:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Remove the &lt;code&gt;&amp;amp;..&lt;&#x2F;code&gt; in the return value and use &lt;code&gt;..&lt;&#x2F;code&gt; instead&lt;&#x2F;li&gt;
&lt;li&gt;Remove the &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt; in the function body&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Some types, like &lt;code&gt;str&lt;&#x2F;code&gt; and &lt;code&gt;Path&lt;&#x2F;code&gt; are only intended to be used with references, and they have a sibling type which is intended to be used only as an owned value. For these types, if we just try removing the &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt; from our code, we&#x27;ll still get compile errors. Here&#x27;s an example that won&#x27;t compile:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; This won&amp;#39;t compile!
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;returns_a_path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() -&amp;gt; Path {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; path: Path = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;somehow_makes_path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();
    path
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For these types which can only be used as references, look for an &lt;code&gt;impl&lt;&#x2F;code&gt; of the trait &lt;code&gt;ToOwned&lt;&#x2F;code&gt;. How it works is &lt;code&gt;ToOwned&lt;&#x2F;code&gt; consumes a shared reference and copies the value into a new owned reference. Here&#x27;s an example of leveraging the &lt;code&gt;ToOwned&lt;&#x2F;code&gt; trait:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;returns_a_pathbuf&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() -&amp;gt; PathBuf {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; path: &amp;amp;Path = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;somehow_makes_path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; pathbuf: PathBuf = path.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();
    pathbuf
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Low Effort&lt;&#x2F;em&gt; - generally, converting a return value from a shared reference to an owned value is really easy. I can usually get something to compile quickly and refactor later using this technique.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Widely Applicable&lt;&#x2F;em&gt; - almost anywhere we can return a reference, we could also return an owned copy of that value&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Safe&lt;&#x2F;em&gt; - a new copy of the value cannot corrupt memory elsewhere, even when moving the data between threads&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Synchronization&lt;&#x2F;em&gt; - this value won&#x27;t be changed if we change the original value.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Memory&lt;&#x2F;em&gt; - we&#x27;ll possibly be wasting memory by making identical copies of some piece of data (usually won&#x27;t happen)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To me, using this technique often feels like a kludge because I&#x27;ve spoken to the Borrow Checker instead of expressing the core logic of my application. Think hard about how many places need to mutate the value in question or if they can all simply read the same value this way. If this is an unchanging value, returning an owned value can be a great solution.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pattern-2-return-boxed-values&quot;&gt;Pattern 2: Return Boxed Values&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s move the database connection off the function stack and into the heap. In our own types, we&#x27;ll need to explicitly heap allocate data using the &lt;code&gt;Box&lt;&#x2F;code&gt; struct from the standard library. So, refactoring our above code to use the heap would look like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) -&amp;gt; Box&amp;lt;Connection&amp;gt; {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; pool: Pool = postgres::connection_pool::new(url);
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; connection: Connection = pool.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();
    connection.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;set_database&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;startup.io&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;);
    Box::new(connection)
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There are two changes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The function signature now has &lt;code&gt;Box&amp;lt;..&amp;gt;&lt;&#x2F;code&gt; as the return type instead of &lt;code&gt;&amp;amp;..&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;The function body instantiates a new box wrapping the connection expression &lt;code&gt;Box::new(..)&lt;&#x2F;code&gt; instead of &lt;code&gt;&amp;amp;..&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The boxed value is an owned struct (not a reference) so it can be returned from the function without angering the borrow checker. When the box itself goes out of scope, the heap allocated memory will be freed.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Memory&lt;&#x2F;em&gt; - The only additional memory (beyond the boxed value) is the pointer to the heap, and pointers are only a few bytes&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Applicability&lt;&#x2F;em&gt; - Almost any code which uses &lt;code&gt;std&lt;&#x2F;code&gt; can leverage this technique&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Indirection&lt;&#x2F;em&gt; - we&#x27;ll need to write more code in our type annotations, and we may need to understand how to leverage the &lt;code&gt;Deref&lt;&#x2F;code&gt; trait to work with boxed values&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Overhead&lt;&#x2F;em&gt; - It&#x27;s more complicated to allocate memory on the heap and this may incur a runtime penalty.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For types like &lt;code&gt;usize&lt;&#x2F;code&gt;, &lt;code&gt;bool&lt;&#x2F;code&gt;, &lt;code&gt;f32&lt;&#x2F;code&gt;, and other primitive types, it can be a code smell if I find myself boxing these values. Instead I normally return a copy of these types instead.&lt;&#x2F;p&gt;
&lt;p&gt;For dynamically growable types like &lt;code&gt;Vec&lt;&#x2F;code&gt;, &lt;code&gt;String&lt;&#x2F;code&gt;, and &lt;code&gt;HashMap&lt;&#x2F;code&gt;, these types already use the heap internally so you&#x27;re not necessarily gaining much by boxing them. If performance or memory usage matter, profile your own code under realistic conditions to determine if boxing your return value improves or degrades performance relative to another pattern.&lt;&#x2F;p&gt;
&lt;p&gt;As with the previous pattern, you want to thing about mutability -- how many places am I reading this value? How many of those places am I going to be writing to this value as well? Boxing in conjunction with Atomic Reference Counting (&lt;code&gt;Arc&lt;&#x2F;code&gt; type) can make mutably sharing a value across threads possible if you need to. If you don&#x27;t need that, boxing might just be slowing down your program or wasting memory.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pattern-3-move-owned-values-to-a-higher-scope&quot;&gt;Pattern 3: Move Owned Values to a Higher Scope&lt;&#x2F;h3&gt;
&lt;p&gt;This technique involves reorganizing code to help us leverage references passed &lt;em&gt;into&lt;&#x2F;em&gt; functions. Here&#x27;s an example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;setup_connection&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;connection&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &amp;amp;Connection) -&amp;gt; &amp;amp;Connection {
   connection.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;set_database&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;startup.io&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;);
   connection
}

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; pool = postgres::connection_pool::new(url);
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;_ in &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; connection = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;setup_connection&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;amp;pool.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;());
      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Do something with connection here
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;}
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We achieved this by:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;moving both the &lt;code&gt;Pool&lt;&#x2F;code&gt; and &lt;code&gt;Connection&lt;&#x2F;code&gt; objects to a scope outside the function&lt;&#x2F;li&gt;
&lt;li&gt;changing the function signature to take in a reference to the connection we wanted to modify&lt;&#x2F;li&gt;
&lt;li&gt;returning a reference to the &lt;em&gt;same&lt;&#x2F;em&gt; memory as the memory borrowed in the argument&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;At the end of &lt;code&gt;setup_connection()&lt;&#x2F;code&gt;, no new structs were declared inside of it. This means nothing needs to be deallocated. Because &lt;code&gt;connection&lt;&#x2F;code&gt; was declared inside the &lt;code&gt;for&lt;&#x2F;code&gt; loop, it lives for one iteration of the loop. In the next iteration, a new connection is allocated. Basically, &lt;strong&gt;we can only get the lifetimes out of a function that we put &lt;em&gt;into&lt;&#x2F;em&gt; the function&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In this particular case, no matter how many times we call &lt;code&gt;setup_connection()&lt;&#x2F;code&gt;, only one &lt;code&gt;Pool&lt;&#x2F;code&gt; object will be allocated, compared to having many pools allocated in the other patterns.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Memory&lt;&#x2F;em&gt; - this pattern avoids heap allocating and writing boilerplate code, so it&#x27;s the memory efficient and elegant&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Aroma&lt;&#x2F;em&gt; - this pattern is often the natural result of good code organization that reduces unnecessary work; its like a good code &amp;quot;aroma&amp;quot;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Complexity&lt;&#x2F;em&gt; - this pattern requires deep understanding of the application and data flows, and usually means rewriting several different areas of the code&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Applicability&lt;&#x2F;em&gt; - many times this pattern can&#x27;t be used&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Rigidness&lt;&#x2F;em&gt; - using this pattern might make refactoring the code more difficult&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This pattern is likely to work when we have an owned value which &amp;quot;emits&amp;quot; other borrowed objects. If we can move the owned struct to a higher scope, we can pass referencecs to helper functions. When I&#x27;m looking to refactor and&#x2F;or reduce memory usage, I examine if this pattern might be an option.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pattern-4-use-callbacks-not-return-values&quot;&gt;Pattern 4: Use Callbacks not Return Values&lt;&#x2F;h3&gt;
&lt;p&gt;This technique was suggested by &lt;a href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;comments&#x2F;5xuq1l&#x2F;strategies_for_returning_references_in_rust&#x2F;delaell&#x2F;&quot;&gt;mmstick on reddit&lt;&#x2F;a&gt;. If you&#x27;ve ever written JavaScript with callbacks or used dependency injection in Java, you&#x27;ll feel right at home here. The basic idea is to &lt;em&gt;avoid fighting the Borrow Checker&lt;&#x2F;em&gt; by passing a closure into the function. Example time:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;connect_and_attempt&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;F&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;pool&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &amp;amp;Pool, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: F) -&amp;gt; Option&amp;lt;String&amp;gt;
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;where&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; F: Fn(Connection) -&amp;gt; Option&amp;lt;String&amp;gt;
{
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; connection: Connection = pool.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();
    connection.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;set_database&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;startup.io&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;);
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(connection)
}

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; pool: Pool = postgres::connection_pool::new(url);
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; result = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;amp;pool, |&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;connection&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;| {
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; do something with connection and return an option
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;Some(output)
    });
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The key difference over pattern 3 is that we pass an anonymous function &lt;code&gt;|connection| { .. }&lt;&#x2F;code&gt; into &lt;code&gt;connect_and_attempt&lt;&#x2F;code&gt; and we never return the &lt;code&gt;connection&lt;&#x2F;code&gt; object at all. This means we didn&#x27;t have to fight the Borrow Checker.&lt;&#x2F;p&gt;
&lt;p&gt;Pros:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Elegant&lt;&#x2F;em&gt; - avoid fighting the borrow checker&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Decoupled&lt;&#x2F;em&gt; - helps isolate application logic from I&#x2F;O or dependencies&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Cons:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Inflexible&lt;&#x2F;em&gt; - closures in Rust are more complicated than JavaScript and might require boxing in certain situations.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Complexity&lt;&#x2F;em&gt; - requires a deeper knowledge of Rust and mastery of closures.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As noted by mmstick, this pattern can make unit testing really easy because we can decouple blocks of code and test their logic without setting up the full environment. We can pass dummy callbacks into the system under test.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;additional-resources&quot;&gt;Additional Resources&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;comments&#x2F;5xuq1l&#x2F;strategies_for_returning_references_in_rust&#x2F;&quot;&gt;Reddit Comments&lt;&#x2F;a&gt; -- I&#x27;m tremedously greateful to all of the many people who chimed in on Reddit about this article -- I learned so much!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.newrustacean.com&#x2F;show_notes&#x2F;e019&#x2F;index.html&quot;&gt;Let&#x27;s Clone a Cow - New Rustacean Podcast&lt;&#x2F;a&gt; - Chris Krycho explains how to do some sophiscated things with memory management in Rust while satisfying the Borrow Checker&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;more-patterns&quot;&gt;More patterns?&lt;&#x2F;h2&gt;
&lt;p&gt;Please, if you have experience using more patterns than I&#x27;ve enumerated here, please share them! I&#x27;d love to incorporate your patterns into this article as a sort of reference of techniques for newbies on the Borrow Checker.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Faster CI Debugging with GitlabCI</title>
		<published>2017-02-24T00:00:00+00:00</published>
		<updated>2017-02-24T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/faster-ci-debugging-with-gitlabci/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/faster-ci-debugging-with-gitlabci/</id>
		<content type="html">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;&quot;&gt;GitlabCI&lt;&#x2F;a&gt; can help you debug many problems that happen in cloud based continuous integration by allowing you to run the build locally. If you&#x27;re leveraging docker to run your builds, it can also eliminate the &amp;quot;It works on my machine -- but not in CI&amp;quot; class of problems as well.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m going to walk you through setting up &lt;code&gt;gitlab-runner&lt;&#x2F;code&gt; locally, and show you how to start using it to speed up your debugging process. I&#x27;ll explain how the architecture of GitlabCI allows us to do build locally, and some gotchas you may encounter.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;assumptions&quot;&gt;Assumptions&lt;&#x2F;h2&gt;
&lt;p&gt;The main assumption is that you&#x27;re interested in or already using Gitlab. Not convinced you should switch? Read &lt;a href=&quot;&#x2F;blog&#x2F;why-i-use-gitlab&#x2F;&quot;&gt;why I use Gitlab&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;you-already-have-gitlabci-setup-for-your-project&quot;&gt;You Already Have GitlabCI Setup for Your Project&lt;&#x2F;h3&gt;
&lt;p&gt;This post isn&#x27;t very helpful unless you&#x27;ve already setup GitlabCI using a .gitlab-ci.yml file. If you haven&#x27;t already done that, checkout the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ce&#x2F;ci&#x2F;quick_start&#x2F;README.html&quot;&gt;getting started guide&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;you-should-install-docker&quot;&gt;You Should Install Docker&lt;&#x2F;h3&gt;
&lt;p&gt;If your builds can run on Linux, you really should be using the Docker executor. Even if your machine runs on Windows, MacOS, or something else that supports Docker, you should install Docker.&lt;&#x2F;p&gt;
&lt;p&gt;The main benefits that accrue to using Docker with GitlabCI are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Docker basically guarrantees that testing on your machine will produce identical results with a remote CI server&lt;&#x2F;em&gt; (which is kinda the whole point of doing this). Without docker, you&#x27;re much less likely to discover why something worked locally but broke in CI.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Docker can ensure that the testing environment configured to our exact specifications.&lt;&#x2F;em&gt; The &lt;code&gt;$PATH&lt;&#x2F;code&gt; can be configured ahead of time, various binaries and utilities installed, DNS configured, security restrictions imposed, etc, etc. The tradeoff is that docker will use more memory and be slower running on bare metal. And docker pull... why can&#x27;t it be faster???&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The main reason you might not care about installing Docker is if you want to build and test on a platform that isn&#x27;t Linux. Docker can entirely capture all aspects of a local Linux system, but it can&#x27;t capture Windows, Mac, Solaris, or BSD systems (to name a few). If you specifically want to reproduce issues occurring on those platforms, Docker won&#x27;t help you. You&#x27;ll need to find some other way to make sure your local machine is an &lt;em&gt;exact&lt;&#x2F;em&gt; replica of the remote CI server so that you can locally debug those issues.&lt;&#x2F;p&gt;
&lt;p&gt;So, if you haven&#x27;t already installed Docker, here&#x27;s how to do it in Linux:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; curl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -sSL&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; https:&#x2F;&#x2F;get.docker.com&#x2F; | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sh
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For MacOS or Windows, go to the &lt;a href=&quot;https:&#x2F;&#x2F;www.docker.com&#x2F;products&#x2F;overview&quot;&gt;Docker download page&lt;&#x2F;a&gt;, download your installer, and run it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-does-gitlab-runner-relate-to-gitlabci&quot;&gt;How Does Gitlab Runner Relate to GitlabCI?&lt;&#x2F;h2&gt;
&lt;p&gt;GitlabCI is composed of a &amp;quot;dashboard&amp;quot; and many &amp;quot;runners&amp;quot;. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-gitlabci-dashboard&quot;&gt;The GitlabCI Dashboard&lt;&#x2F;h3&gt;
&lt;p&gt;The dashboard is part of Gitlab and can be controlled by:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;doing a git push to your gitlab instance&lt;&#x2F;li&gt;
&lt;li&gt;starting, cancelling, or retrying a pipeline in the web interface&lt;&#x2F;li&gt;
&lt;li&gt;making API requests to the gitlab instance&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The dashboard is the command and control center. It doesn&#x27;t do the work; it just delegates and supervises a pool of runners. When the dashboard receives instructions to start a pipeline, it adds that pipeline to a queue, and then feeds work from the queue to available runners.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-gitlab-runners&quot;&gt;The Gitlab Runners&lt;&#x2F;h3&gt;
&lt;p&gt;The runners are individual computers that are running the Gitlab Runner binary. Typically, runners register themselves with the dashboard using a secure token generated by the dashboard. Runners can have names and tags; they receive instructions from the dashboard, execute the command and continuously report back results of running pipelines and jobs. It&#x27;s very important that registered runners have as close to 100% network connectivity as possible to ensure that jobs can be run on demand. Typically, the runners are installed as a system service or daemonized.&lt;&#x2F;p&gt;
&lt;p&gt;Runners can also operate without connecting to a Gitlab instance. In this offline mode, they have limited functionality, but they can read a local git repository and directly execute jobs listed in a .gitlab-ci.yml. Offline runners must be invoked directly on the command line to execute; they don&#x27;t run daemonized in this mode.&lt;&#x2F;p&gt;
&lt;p&gt;In this guide, we&#x27;ll be installing Gitlab Runner on your local machine and running in this offline mode to debug CI problems locally.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;install-gitlab-runner-on-macos&quot;&gt;Install Gitlab Runner on MacOS&lt;&#x2F;h2&gt;
&lt;p&gt;At time of writing these are the official steps:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo curl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --output&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; &#x2F;usr&#x2F;local&#x2F;bin&#x2F;gitlab-ci-multi-runner https:&#x2F;&#x2F;gitlab-ci-multi-runner-downloads.s3.amazonaws.com&#x2F;latest&#x2F;binaries&#x2F;gitlab-ci-multi-runner-darwin-amd64
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo chmod +x &#x2F;usr&#x2F;local&#x2F;bin&#x2F;gitlab-ci-multi-runner
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;NOTE: For the most current instructions, check the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;runner&#x2F;install&#x2F;osx.html&quot;&gt;official documentation&lt;&#x2F;a&gt;.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, Gitlab Runner has different names on MacOS and Linux. I tend to prefer the shorter name, so I&#x27;d recommend you add a symlink in your path to the Linux name (&lt;code&gt;gitlab-runner&lt;&#x2F;code&gt;). &lt;strong&gt;I&#x27;ll be referring to the command as &lt;code&gt;gitlab-runner&lt;&#x2F;code&gt; in the rest of this post, so if you don&#x27;t symlink as recommended below, use &lt;code&gt;gitlab-ci-multi-runner&lt;&#x2F;code&gt; in your terminal instead of &lt;code&gt;gitlab-runner&lt;&#x2F;code&gt;.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo ln&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; &#x2F;usr&#x2F;local&#x2F;bin&#x2F;gitlab-ci-multi-runner &#x2F;usr&#x2F;local&#x2F;bin&#x2F;gitlab-runner
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; which gitlab-runner
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;&#x2F;usr&#x2F;local&#x2F;bin&#x2F;gitlab-runner
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;install-gitlab-runner-on-ubuntu&quot;&gt;Install Gitlab Runner on Ubuntu&lt;&#x2F;h2&gt;
&lt;p&gt;For Ubuntu (and presumably Debian as well), you&#x27;ll want to use Gitlab&#x27;s PPA so that you&#x27;re always on a current version of GitLab Runner. At the time of writing, you can add the Gitlab PPA like so:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; curl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -L&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; https:&#x2F;&#x2F;packages.gitlab.com&#x2F;install&#x2F;repositories&#x2F;runner&#x2F;gitlab-ci-multi-runner&#x2F;script.deb.sh | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sudo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; bash
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(Note that this may change over time. Here&#x27;s the current &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;runner&#x2F;install&#x2F;linux-repository.html&quot;&gt;installation instructions&lt;&#x2F;a&gt; for Gitlab Runner.)&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s install &lt;code&gt;gitlab-runner&lt;&#x2F;code&gt; itself now:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo apt-get install gitlab-ci-multi-runner
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;double-check-that-gitlab-runner-is-working&quot;&gt;Double Check that Gitlab Runner is working&lt;&#x2F;h2&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; gitlab-runner
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You should see output something like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;NAME:
   gitlab-runner&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; - a GitLab Runner

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;USAGE:
   gitlab-runner &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;global options&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; command &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;command options&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;] [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;arguments...&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;VERSION:
   1.11.0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; (33af656)

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;AUTHOR&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(S)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;:
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;GitLab&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; Inc. &amp;lt;support@gitlab.com&amp;gt; 

COMMANDS:
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;exec&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;			execute a build locally
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;ve shortened the output considerably. Basically, you should see lots of help text explaining how this command works.&lt;&#x2F;p&gt;
&lt;p&gt;If you don&#x27;t see help text and see an error message instead, reach out to me on Twitter or in the comments, or consider reaching out to Gitlab for support.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;run-a-job-with-gitlab-runner&quot;&gt;Run a Job with gitlab-runner&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s assume you have .gitlab-ci.yml file already committed that looks like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;image: node:latest

my_special_tests:
  script:
    - npm install
    - npm test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can run the job &lt;code&gt;my_special_tests&lt;&#x2F;code&gt; like so:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; cd path&#x2F;to&#x2F;project
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; ls .gitlab-ci.yml
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;.gitlab-ci.yml
$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; gitlab-runner exec docker my_special_tests
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The output of that command will look nearly identical to what you normally see on gitlab.com in your job page.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s break down the above syntax a little bit:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cd ...&lt;&#x2F;code&gt; - just make sure you&#x27;re in the root directory of your project&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;ls .gitlab-ci.yml&lt;&#x2F;code&gt; - there should already be a .gitlab-ci.yml file in this directory&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;gitlab-runner exec docker tests&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gitlab-runner&lt;&#x2F;code&gt; - this is main command that allows you to control the gitlab-runner. This is same code used in a remote environment to run your tests on gitlab.com and on your own instances of gitlab.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;exec&lt;&#x2F;code&gt; - this subcommand allows us to work directly with our local git repo and execute builds without doing lots of fancy setup. It&#x27;s quick and dirty -- exactly what we want for quick debugging&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;docker&lt;&#x2F;code&gt; - this means &amp;quot;use the docker executor to run this build&amp;quot;. This is the preferred way to run builds. However if you need to run builds on MacOS or Windows, you&#x27;ll probably want to replace with &lt;code&gt;shell&lt;&#x2F;code&gt; instead. You just won&#x27;t get many guarrantees about the environment your shell runs in.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;my_special_tests&lt;&#x2F;code&gt; - this is the name of the test to run taken from the .gitlab-ci.yml file. If you had a job named &#x27;foobar&#x27; you&#x27;d repalce &lt;code&gt;my_special_tests&lt;&#x2F;code&gt; with &lt;code&gt;foobar&lt;&#x2F;code&gt; instead.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;gitlab-runner-exec-gotchas&quot;&gt;Gitlab Runner Exec Gotchas&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Even your local changes &lt;strong&gt;must&lt;&#x2F;strong&gt; be committed to git or they won&#x27;t be available to gitlab-runner&lt;&#x2F;li&gt;
&lt;li&gt;The cache and artifacts in your .gitlab-ci.yml won&#x27;t work with the &lt;code&gt;gitlab-runner exec docker&lt;&#x2F;code&gt; command&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;protips&quot;&gt;Protips&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Run gitlab-runner locally any time you&#x27;re initially setting up a new .gitlab-ci.yml&lt;&#x2F;strong&gt; file. You can catch typos in your shell scripting or find problems with your &lt;code&gt;$PATH&lt;&#x2F;code&gt; in a few seconds or minutes instead of hours.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Leverage Docker&lt;&#x2F;strong&gt; - you&#x27;ll be able to debug problems from a machine using the big 3 operating systems. You&#x27;ll also be guarranteed an identical build in your remote CI.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Fallback to deploying from local machines&lt;&#x2F;strong&gt; if your cloud based CI goes down; just make sure to use a password manager like LastPass or 1Password to store deployment secrets, and document the procedure. This can be a really nice way to reduce a single point of failure.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;While CircleCI, TravisCI, and Jenkins have many fantastic features, its very difficult to get into a &amp;quot;flow&amp;quot; state when debugging them. This can be especially true if many other jobs are running at the same time. GitlabCI lets you do end-run around contention in CI and get results much faster. As a side benefit, when GitlabCI goes down, you can continue to run your deploy scripts locally by invoking Gitlab Runner directly. Setup Gitlab Runner right now and speed up your own debugging speed.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Why I Use GitlabCI</title>
		<published>2017-02-01T00:00:00+00:00</published>
		<updated>2017-02-01T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/why-i-use-gitlab/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/why-i-use-gitlab/</id>
		<content type="html">&lt;p&gt;I&#x27;ve started moving all my projects away from Github in favor of Gitlab, and I haven&#x27;t looked back.&lt;&#x2F;p&gt;
&lt;p&gt;I should say upfront that I&#x27;m not affiliated with nor employed by Gitlab. I haven&#x27;t been paid, prompted, or asked by Gitlab to write this post. And, as far as I know, Gitlab has no idea I&#x27;ve written about them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-favorite-gitlab-features&quot;&gt;My favorite Gitlab Features&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Unlimited free closed projects, like Bitbucket&lt;&#x2F;li&gt;
&lt;li&gt;Painless Github importer&lt;&#x2F;li&gt;
&lt;li&gt;Remove source branch after merge to master automatically&lt;&#x2F;li&gt;
&lt;li&gt;Merge branch when tests pass automatically&lt;&#x2F;li&gt;
&lt;li&gt;Create a branch from a issue in website; auto-resolve issue when branch is merged&lt;&#x2F;li&gt;
&lt;li&gt;Integrated approach to software development makes it easier see what&#x27;s happening in my project&lt;&#x2F;li&gt;
&lt;li&gt;Excellent issue tracker &#x2F; basic project management&lt;&#x2F;li&gt;
&lt;li&gt;Integrated CI with excellent documentation&lt;&#x2F;li&gt;
&lt;li&gt;Proper docker support in CI (not a forked build of docker)&lt;&#x2F;li&gt;
&lt;li&gt;Builtin Docker image registry&lt;&#x2F;li&gt;
&lt;li&gt;Build environment can be any docker image&lt;&#x2F;li&gt;
&lt;li&gt;Parallel builds in CI&lt;&#x2F;li&gt;
&lt;li&gt;API for CI&lt;&#x2F;li&gt;
&lt;li&gt;CI Deployments track where your code is deployed to (I use this feature to render previews of my blog on S3 on a per branch basis)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;gitlab-is-more-innovative&quot;&gt;Gitlab is more innovative&lt;&#x2F;h2&gt;
&lt;p&gt;Gitlab has pioneered a plethora of new features before Github, Bitbucket, Gogs or any of the other competitors in the space.  Gitlab&#x27;s commitment to open source &lt;em&gt;product direction&lt;&#x2F;em&gt;, incident response, and source code means that I have insight into the product roadmap as well as the ability to contribute my feedback and features.&lt;&#x2F;p&gt;
&lt;p&gt;By comparison, Github has more-or-less copied a few of the best project management features but still lags behind Gitlab in terms of basic features like &amp;quot;Merge on Test Pass&amp;quot; and &amp;quot;Remove Source Branch on Merge&amp;quot; features. Github itself is 100% closed source and notorious for its lack of transparency in terms of product direction. Github eventually added the ability to rebase-merge, and create issue templates months and months after Gitlab. By choosing to use Gitlab, you&#x27;re getting the best new features right away.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gitlabci-is-competitive&quot;&gt;GitlabCI is competitive&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s talk about GitlabCI. Here&#x27;s a quick comparison (due to age poorly) between the major continuous integration and continuous deployment options in the space:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;CI&#x2F;CD Service&lt;&#x2F;th&gt;&lt;th&gt;GitlabCI&lt;&#x2F;th&gt;&lt;th&gt;TravisCI&lt;&#x2F;th&gt;&lt;th&gt;CircleCI&lt;&#x2F;th&gt;&lt;th&gt;Codeship&lt;&#x2F;th&gt;&lt;th&gt;DroneCI&lt;&#x2F;th&gt;&lt;th&gt;Jenkins&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;VCS Support&lt;&#x2F;td&gt;&lt;td&gt;git&lt;&#x2F;td&gt;&lt;td&gt;git&lt;&#x2F;td&gt;&lt;td&gt;git&lt;&#x2F;td&gt;&lt;td&gt;git&lt;&#x2F;td&gt;&lt;td&gt;git&lt;&#x2F;td&gt;&lt;td&gt;any via plugin&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Github Support&lt;&#x2F;td&gt;&lt;td&gt;no, but..&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;plugin&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Gitlab Support&lt;&#x2F;td&gt;&lt;td&gt;gitlab&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;plugin&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Bitbucket Support&lt;&#x2F;td&gt;&lt;td&gt;no, but..&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;plugin&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Open Source&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No?&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Founded&lt;&#x2F;td&gt;&lt;td&gt;2011&lt;&#x2F;td&gt;&lt;td&gt;2011&lt;&#x2F;td&gt;&lt;td&gt;2011&lt;&#x2F;td&gt;&lt;td&gt;2011&lt;&#x2F;td&gt;&lt;td&gt;???&lt;&#x2F;td&gt;&lt;td&gt;2004 (as Hudson)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Documentation Style&lt;&#x2F;td&gt;&lt;td&gt;Reference&lt;&#x2F;td&gt;&lt;td&gt;Tutorial&lt;&#x2F;td&gt;&lt;td&gt;Tutorial&lt;&#x2F;td&gt;&lt;td&gt;Tutorial&lt;&#x2F;td&gt;&lt;td&gt;Reference&lt;&#x2F;td&gt;&lt;td&gt;Tutorial&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Documentation Search&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Cloud Option&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;CloudBees&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;On-premise Option&lt;&#x2F;td&gt;&lt;td&gt;CE &amp;amp; EE&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;EE&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Local execution&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Maybe...&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Maybe...&lt;&#x2F;td&gt;&lt;td&gt;Docker&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Debugging&lt;&#x2F;td&gt;&lt;td&gt;Local&lt;&#x2F;td&gt;&lt;td&gt;SSH&lt;&#x2F;td&gt;&lt;td&gt;SSH&lt;&#x2F;td&gt;&lt;td&gt;Local&lt;&#x2F;td&gt;&lt;td&gt;Web&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Linux Test env&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;OSX Test env&lt;&#x2F;td&gt;&lt;td&gt;Not Cloud&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Not Cloud&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Windows Test env&lt;&#x2F;td&gt;&lt;td&gt;Not Cloud&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;Not Cloud&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Docker support&lt;&#x2F;td&gt;&lt;td&gt;BEST EVER&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Awful&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Enterprise Support&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;Probably&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Free Closed Source Projects&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;1 project&lt;&#x2F;td&gt;&lt;td&gt;On prem&lt;&#x2F;td&gt;&lt;td&gt;On prem&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Build API&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Manual Test&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Source Controlled Config&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Secrets management&lt;&#x2F;td&gt;&lt;td&gt;Web&lt;&#x2F;td&gt;&lt;td&gt;YML &amp;amp; Web&lt;&#x2F;td&gt;&lt;td&gt;Web&lt;&#x2F;td&gt;&lt;td&gt;Web&amp;amp;docker&lt;&#x2F;td&gt;&lt;td&gt;Web&lt;&#x2F;td&gt;&lt;td&gt;Web&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Artifacts&lt;&#x2F;td&gt;&lt;td&gt;Native&lt;&#x2F;td&gt;&lt;td&gt;S3&lt;&#x2F;td&gt;&lt;td&gt;Native&lt;&#x2F;td&gt;&lt;td&gt;S3&#x2F;SFTP&lt;&#x2F;td&gt;&lt;td&gt;S3&#x2F;Heroku&lt;&#x2F;td&gt;&lt;td&gt;plugins&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Parallel Build &amp;quot;Steps&amp;quot;&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Parallel step rerun&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Parallel synchronization&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Build notifications&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Integrated Docker Registry&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Deployment targets&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;3rd party&lt;&#x2F;td&gt;&lt;td&gt;3rd party&lt;&#x2F;td&gt;&lt;td&gt;3rd Party&lt;&#x2F;td&gt;&lt;td&gt;3rd party&lt;&#x2F;td&gt;&lt;td&gt;plugins&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Badges&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;plugin&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Integrated Webhooks&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;One of the key differentiators above is the ability to run the CI locally. This is really really handy for two reasons:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Increased fault tolerance: even if your remote CI infrastructure is down, you can still run builds and deployments.&lt;&#x2F;li&gt;
&lt;li&gt;Debugging: its potentially much faster to iterate on a broken test locally&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Both GitlabCI and Codeship are designed to help with the second bullet point. GitlabCI also provides the increased fault tolerance that I often wanted when I relied on CircleCI.&lt;&#x2F;p&gt;
&lt;p&gt;For me, GitlabCI is the most exciting CI option available, especially in the build-on-mac-and-deploy-to-linux scenario.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;Gitlab unifies all (or most) of the different services you previously relied on when using Github, but is flexible enough to work with other systems (via triggers, API, and webhooks) or simply does a better job than Github + other services. If you&#x27;re thinking about making the switch, I&#x27;d encourage you to try out the gitlab.com for your next side project or open source project.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>A Quick Intro to the NERDTree plugin for Vim</title>
		<published>2016-08-04T00:00:00+00:00</published>
		<updated>2016-08-04T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/quick-intro-to-nerdtree-plugin-vim/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/quick-intro-to-nerdtree-plugin-vim/</id>
		<content type="html">&lt;p&gt;NERDTree seems to be the &lt;em&gt;de facto&lt;&#x2F;em&gt; file explorer for Vim, but it&#x27;s hard to find a good introduction to its quirks and some basic configurations without duckduckgo&#x27;ing tons of blog posts and reverse engineering individual &lt;code&gt;.vimrc&lt;&#x2F;code&gt;s. This post tries to compile all the things I learned over several hours into an easily digestable summary.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;show-hidden-files&quot;&gt;Show Hidden Files&lt;&#x2F;h2&gt;
&lt;p&gt;By default, NERDTree hides dot files. For many developers, this is a frustrating default setting that makes it hard to open and edit .gitignore, .travis.yml, and other really important dotfiles. I couldn&#x27;t find any documentation about how to change the default in my &lt;code&gt;.vimrc&lt;&#x2F;code&gt;, but here&#x27;s how to do it:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot; .vimrc
let NERDTreeShowHidden=1
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;filter-out-custom-files-and-directories&quot;&gt;Filter Out Custom Files and Directories&lt;&#x2F;h2&gt;
&lt;p&gt;The syntax is a little weird, so let&#x27;s look at an example and then break it down:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot; .vimrc
let NERDTreeIgnore=[&amp;#39;.git$[[dir]]&amp;#39;, &amp;#39;.swp&amp;#39;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;Use the variable &lt;code&gt;NERDTreeIgnore&lt;&#x2F;code&gt; to configure the filters&lt;&#x2F;li&gt;
&lt;li&gt;Specify an array of patterns to ignore, using &lt;code&gt;[&lt;&#x2F;code&gt; and &lt;code&gt;]&lt;&#x2F;code&gt; to bookend the list of comma delimited patterns&lt;&#x2F;li&gt;
&lt;li&gt;Files should be specified as string literals (ex: &lt;code&gt;&#x27;.swp&#x27;&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Directories should be specified as string literals with the magic suffix &lt;code&gt;$[[dir]]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;m guessing that glob patterns are also supported, but I haven&#x27;t experimented with that yet. Open a PR on this article if you have experience with globs!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;update-nerdtree-s-view-of-files&quot;&gt;Update NERDTree&#x27;s View of Files&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Click on the NERDTree buffer&lt;&#x2F;li&gt;
&lt;li&gt;Press &lt;code&gt;R&lt;&#x2F;code&gt; or &lt;code&gt;r&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Full story&lt;&#x2F;strong&gt; For performance reasons, NERDTree caches its view of the file system. This means that if you add, rename, move, or delete a file outside NERDTree, you won&#x27;t be able to see that change reflected.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adding-renaming-moving-and-deleting-files-and-directories&quot;&gt;Adding, Renaming, Moving, and Deleting Files and Directories&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Click on the NERDTree buffer&lt;&#x2F;li&gt;
&lt;li&gt;Press &lt;code&gt;m&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Pick the option you want&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;This is all super basic NERDTree stuff, but I couldn&#x27;t find a good summary elsewhere. What are you advanced tricks to get the most of NERDTree?&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Builtin Neovim Features Everyone Should Know</title>
		<published>2016-07-19T00:00:00+00:00</published>
		<updated>2016-07-19T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/builtin-neovim-features-everyone-should-know/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/builtin-neovim-features-everyone-should-know/</id>
		<content type="html">&lt;p&gt;For longtime (Neo)Vim enthusiasts, nothing here will blow your mind. However, if you&#x27;ve always felt like there are more builtin features of Vim you&#x27;re missing out on, here&#x27;s a handful of useful features I&#x27;d overlooked for years which don&#x27;t require any plugins.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;neovim-s-builtin-terminal-emulator&quot;&gt;Neovim&#x27;s Builtin Terminal Emulator&lt;&#x2F;h2&gt;
&lt;p&gt;This is probably the most killer built-in upgrade that Neovim offers over standard Vim. Basically, Neovim exposes a new &amp;quot;terminal&amp;quot; mode that forwards all your keystrokes to an underlying terminal (which is handled by a cross-platform terminal emulator library).&lt;&#x2F;p&gt;
&lt;p&gt;Compared to using tmux + Vim, Neovim&#x27;s terminal editor lets you keep all your buffers (and terminal) in a single Vim instance. So your jump list, yanks, and global state are shared. For folks like me too lazy to learn tmux, I can cross that off my list.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;starting-terminal-mode-in-the-current-buffer&quot;&gt;Starting Terminal Mode in the Current Buffer&lt;&#x2F;h3&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;:terminal
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then type anything that will put you into insert mode (&lt;code&gt;a&lt;&#x2F;code&gt;, &lt;code&gt;o&lt;&#x2F;code&gt;, &lt;code&gt;i&lt;&#x2F;code&gt;, etc).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;start-terminal-mode-in-a-new-buffer&quot;&gt;Start Terminal Mode in a New Buffer&lt;&#x2F;h3&gt;
&lt;p&gt;If you&#x27;d rather split the current buffer and start a terminal in that, type this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;:sp&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; term:&#x2F;&#x2F;bash
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then click or &lt;code&gt;&amp;lt;C-W&amp;gt;h&lt;&#x2F;code&gt; into the new terminal buffer and use insert mode.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;exiting-terminal-mode&quot;&gt;Exiting Terminal Mode&lt;&#x2F;h3&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;C-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;C-n&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Personally, I find that a bit too cumbersome, so I&#x27;m definitely going to remap that to something else.&lt;&#x2F;p&gt;
&lt;p&gt;To learn more about terminal mode, see the &lt;a href=&quot;https:&#x2F;&#x2F;neovim.io&#x2F;doc&#x2F;user&#x2F;nvim_terminal_emulator.html&quot;&gt;official docs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;deciphering-vim-s-key-notation&quot;&gt;Deciphering Vim&#x27;s Key Notation&lt;&#x2F;h2&gt;
&lt;p&gt;I found myself perpetually confused by the key notation I&#x27;d see everywhere online and in the builtin docs. Finally, I determined to search until I found an explanation. Here&#x27;s a &lt;a href=&quot;http:&#x2F;&#x2F;vim.wikia.com&#x2F;wiki&#x2F;Mapping_keys_in_Vim_-_Tutorial_(Part_2)#Key_notation&quot;&gt;semi-official wikia entry&lt;&#x2F;a&gt; which does a great job of explaining. Here&#x27;s the major takeaways:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;C-...&amp;gt;&lt;&#x2F;code&gt; means CTRL and ... at the same time. Ex: &lt;code&gt;&amp;lt;C-k&amp;gt;&lt;&#x2F;code&gt; = CTRL+k&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;S-...&amp;gt;&lt;&#x2F;code&gt; means Shift and ... at the same time.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;A-...&amp;gt;&lt;&#x2F;code&gt; means Alt and ... at the same time.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;BS&amp;gt;&lt;&#x2F;code&gt; is backspace (not profanity...silly I know).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;F1&amp;gt;&lt;&#x2F;code&gt; is the F1 key. No shocker there, just mentioning it in case you can&#x27;t easily access your function keys.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;ENTER&amp;gt;&lt;&#x2F;code&gt; is the enter key.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Additionally, you can always escape a single key stroke in insert or normal mode (and possibly other modes too) by pressing CTRL-V first. For example, CTRL-V and ENTER becomes &lt;code&gt;&lt;&#x2F;code&gt; which is the same as &lt;code&gt;&amp;lt;ENTER&amp;gt;&lt;&#x2F;code&gt; but a lot fewer keystrokes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;vim-s-visual-blockwise-edit&quot;&gt;Vim&#x27;s Visual Blockwise Edit&lt;&#x2F;h2&gt;
&lt;p&gt;Seriously...how did I not know about this? How?! Basically, you can &lt;em&gt;visually&lt;&#x2F;em&gt; select a rectangular region of the current buffer and make the same change to all of them simultaneously. This is a really fast way to change a list of items that start with a single quote into a list of items that start with double quotes, or other tedious tasks which are difficult to change by using regex. Here&#x27;s a quick example of visual blockwise edit in action:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2016&#x2F;vim-blockwise-edit.gif&quot; alt=&quot;Visual blockwise edit in vim&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In that gif, I pressed &lt;code&gt;CTRL-V&lt;&#x2F;code&gt; (sorry, using the mouse doesn&#x27;t work by default) in insert mode to start visually selecting. Next I pressed &lt;code&gt;l&lt;&#x2F;code&gt; three times. Then I pressed &lt;code&gt;j&lt;&#x2F;code&gt; repeatedly to select down several rows. Instead of the selection wrapping around the end of the current line, a &amp;quot;block&amp;quot; of text is selected straight down. Any operator will be previewed on the first line but applied to all lines of the block selection. I pressed &lt;code&gt;x&lt;&#x2F;code&gt; to delete the entire selection.&lt;&#x2F;p&gt;
&lt;p&gt;Another idea that you might try is changing the whole selection with &lt;code&gt;c&lt;&#x2F;code&gt; followed by &lt;code&gt;ESC&lt;&#x2F;code&gt; after your changes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;search-and-replace-within-a-selection&quot;&gt;Search and Replace Within a Selection&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Use the cursor to select (or press &lt;code&gt;v&lt;&#x2F;code&gt; and use motion commands) to select a block of text&lt;&#x2F;li&gt;
&lt;li&gt;Press &lt;code&gt;:&lt;&#x2F;code&gt;. You should the statusline show &lt;code&gt;:&#x27;&amp;lt;,&#x27;&amp;gt;&lt;&#x2F;code&gt; which simply means the selection&lt;&#x2F;li&gt;
&lt;li&gt;Type &lt;code&gt;:s&#x2F;old&#x2F;new&#x2F;g&lt;&#x2F;code&gt; and press &lt;code&gt;&amp;lt;ENTER&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;vim-s-ways-of-moving&quot;&gt;Vim&#x27;s Ways of Moving&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Go to line (normal mode). Type the line number then capital G. Ex: &lt;code&gt;200G&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Ignore case for only one search. Add &lt;code&gt;\c&lt;&#x2F;code&gt; to the end (for &amp;quot;case&amp;quot;). Ex: &lt;code&gt;&#x2F;FOOBAR\c&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Jump a paragraph forward &lt;code&gt;}&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Jump a section forward &lt;code&gt;]]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Jump back to a previous jump - &lt;code&gt;CTRL-O&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Jump forward to a previous jump - &lt;code&gt;CTRL-I&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A &amp;quot;jump&amp;quot; can be triggered by using &lt;code&gt;&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;}&lt;&#x2F;code&gt;, &lt;code&gt;]]&lt;&#x2F;code&gt; and other forms of movement. See &lt;code&gt;:help jumps&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Writing this article made me realize how much of Vim I&#x27;m not taking advantage of, and that it always pays to invest a little more time and effort into your tools. What&#x27;s your favorite builtin features of (neo)vim? I&#x27;d love to hear in the comments. Better yet, open a PR against this article with your corrections and additions.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Golang Testing stdlib Errors</title>
		<published>2016-04-05T00:00:00+00:00</published>
		<updated>2016-04-05T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/golang-testing-stdlib-errors/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/golang-testing-stdlib-errors/</id>
		<content type="html">&lt;p&gt;It can be tough to get anywhere close to 100% code coverage when funcs in the &amp;quot;stdlib&amp;quot; can&#x27;t be forced to error. This post takes you through several of the common funcs built in to golang and shows you how to trigger an error to help you bump up your code coverage. This list is short and skews heavily toward http, where I spend most of my time.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;encoding-json-marshal&quot;&gt;encoding&#x2F;json.Marshal&lt;&#x2F;h2&gt;
&lt;p&gt;Marshal any &lt;code&gt;func&lt;&#x2F;code&gt;, &lt;code&gt;chan&lt;&#x2F;code&gt;, or &lt;code&gt;complex&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-golang&quot; data-lang=&quot;golang&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;serialized, err := json.Marshal(func(){})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;http:&#x2F;&#x2F;play.golang.org&#x2F;p&#x2F;FuQyHNHYNY&quot;&gt;See in the playground&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;net-url-parse&quot;&gt;net&#x2F;url.Parse()&lt;&#x2F;h2&gt;
&lt;p&gt;Pass invalid hex strings:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-golang&quot; data-lang=&quot;golang&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;u, err := url.Parse(&amp;quot;%zzzzz&amp;quot;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;http:&#x2F;&#x2F;play.golang.org&#x2F;p&#x2F;OJk3B-86c3&quot;&gt;See in the playground&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;golang.org&#x2F;src&#x2F;net&#x2F;url&#x2F;url_test.go#L705&quot;&gt;See unit tests for unescape&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;net-http-newrequest&quot;&gt;net&#x2F;http.NewRequest()&lt;&#x2F;h2&gt;
&lt;p&gt;The url in &lt;code&gt;NewRequest()&lt;&#x2F;code&gt; also goes through &lt;code&gt;url.Parse()&lt;&#x2F;code&gt;, so we can reuse our url breaking tricks.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-golang&quot; data-lang=&quot;golang&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;r, err := http.NewRequest(&amp;quot;GET&amp;quot;,&amp;quot;&#x2F;%zz&amp;quot;, nil)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;http:&#x2F;&#x2F;play.golang.org&#x2F;p&#x2F;Xkr8IAYo8S&quot;&gt;See in the playground&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;net-http-listenandserve&quot;&gt;net&#x2F;http.ListenAndServe()&lt;&#x2F;h2&gt;
&lt;p&gt;We can pass an invalid port number to trip up &lt;code&gt;ListenAndServe()&lt;&#x2F;code&gt;. Full working example this time since this won&#x27;t run in the playground.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-golang&quot; data-lang=&quot;golang&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;package main

import (
  &amp;quot;net&#x2F;http&amp;quot;
  &amp;quot;log&amp;quot;
)

func main() {
  h := func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte(`Hello World`))
  }
  http.HandleFunc(&amp;quot;&#x2F;&amp;quot;, h)
  log.Fatal(http.ListenAndServe(&amp;quot;:9999999&amp;quot;, nil))
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If choosing the port is not an option, we can &lt;strong&gt;cheat&lt;&#x2F;strong&gt; on our unit test, and block that port on the host. However, the stdlib doesn&#x27;t support this in any form, so you&#x27;d have to pick a stoppable http server like &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tylerb&#x2F;graceful&quot;&gt;tylerb&#x2F;graceful&lt;&#x2F;a&gt;, like so:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-golang&quot; data-lang=&quot;golang&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;package main

import (
  &amp;quot;fmt&amp;quot;
  &amp;quot;net&#x2F;http&amp;quot;

  &amp;quot;gopkg.in&#x2F;tylerb&#x2F;graceful.v1&amp;quot;
)

func main() {
  &#x2F;&#x2F; First block the port we care about
  h1 := func(w http.ResponseWriter, r *http.Request) {
    w.Write([]bytes(`Blocked`))
  }
  srv := &amp;amp;graceful.Server{
    Timeout: 5 * time.Second,
    Server: &amp;amp;http.Server{
      Addr: &amp;quot;:1234&amp;quot;,
      Handler: http.HandlerFunc(h1)
    },
  }
  srv.ListenAndServe()

  &#x2F;&#x2F; Now try to use the blocked port
  h2 := func(w http.ResponseWriter, r *http.Request) {
    w.Write([]bytes(`Blocked`))
  }
  fmt.Println(http.ListenAndServe(&amp;quot;:1234&amp;quot;, http.HandlerFunc(h2))

  &#x2F;&#x2F; Stop the blocking server
  srv.Stop()
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;net-http-client&quot;&gt;net&#x2F;http.Client&lt;&#x2F;h2&gt;
&lt;p&gt;Often, you want a way to force a connection error to simulate problems with some API you&#x27;re consuming as part of your service. Golang&#x27;s stdlib provides the &lt;code&gt;RoundTripper&lt;&#x2F;code&gt; interface to allow us to customize interaction with the network.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-golang&quot; data-lang=&quot;golang&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;package main

import (
  &amp;quot;errors&amp;quot;
  &amp;quot;fmt&amp;quot;
  &amp;quot;net&#x2F;http&amp;quot;
)

&#x2F;&#x2F; Force errors or responses of our choosing to be returned
type mock struct {}
func (s *mock) RoundTrip(r *http.Request) (*http.Response, error) {
  return nil, errors.New(&amp;quot;Unable to connect&amp;quot;)
}

func main() {
  client := &amp;amp;http.Client{ &amp;amp;mockRoundTripper{}, nil, nil, 0 }
  resp, err := client.Get(&amp;quot;https:&#x2F;&#x2F;www.google.com&amp;quot;)
  fmt.Println(&amp;quot;resp=&amp;quot;, resp)
  fmt.Println(&amp;quot;err=&amp;quot;, err)
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;what-else&quot;&gt;What Else?&lt;&#x2F;h2&gt;
&lt;p&gt;If you run into a golang func that you can&#x27;t break, post it in the comments here or tweet it to me.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Essentials for Cloud Deployment</title>
		<published>2016-01-05T00:00:00+00:00</published>
		<updated>2016-01-05T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/essentials-for-cloud-deployment/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/essentials-for-cloud-deployment/</id>
		<content type="html">&lt;p&gt;The most critical thing a deployment pipeline can do is instill confidence in your team. Build your pipeline on a bedrock of credible integration tests. Produce those results in an expedient manner, and deliver your software to customers immediately. When your developers believe in their deployment pipeline system, they can achieve great things.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;credible-integration-tests&quot;&gt;Credible Integration Tests&lt;&#x2F;h2&gt;
&lt;p&gt;Unit tests are not worth very much. Only tests which walk through the exact scenarios your customers will actually use on their actual data in your network can tell you if you&#x27;ve done your work correctly.&lt;&#x2F;p&gt;
&lt;p&gt;In a prior life, my team developed an axiom:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Master is always deployable&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This rule led us inexorably to the following rule:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Only tested code can be merged to master&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;But to satisfy this condition, arbitrary branches must be deployable to a &amp;quot;real&amp;quot; environment. Most often, production incidents occurred because our &amp;quot;real&amp;quot; environments were not real enough. For instance, I once personally wrote a feature to validate account data before saving it to the database. However, old customer accounts were &amp;quot;invalid&amp;quot; according to my feature, and we received many complaints from customers unable to login days later.&lt;&#x2F;p&gt;
&lt;p&gt;How could this happen? I had written unit tests, integration tests, test fixtures, code review, and manually QA&#x27;d the work in a remote environment. The problem was that I had not tested using the production database. I could tell many similar stories of trying to migrate logs files to new block devices, &amp;quot;optimizing&amp;quot; database queries, or adding load balancers and breaking deployments.&lt;&#x2F;p&gt;
&lt;p&gt;The point is your tests can&#x27;t save you from catastrophic disasters if you can&#x27;t 100% trust them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;100-trusting-your-tests&quot;&gt;100% Trusting Your Tests&lt;&#x2F;h3&gt;
&lt;p&gt;Here&#x27;s a(n incomplete) list of things that are prerequisites for trusting your integration tests:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Remote servers&lt;&#x2F;li&gt;
&lt;li&gt;Production traffic&lt;&#x2F;li&gt;
&lt;li&gt;Real data&lt;&#x2F;li&gt;
&lt;li&gt;Real network topology&lt;&#x2F;li&gt;
&lt;li&gt;Happy and sad path testing&lt;&#x2F;li&gt;
&lt;li&gt;&amp;quot;Production&amp;quot; mode&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;remote-servers-real-network-topology&quot;&gt;Remote Servers + Real Network Topology&lt;&#x2F;h4&gt;
&lt;p&gt;Docker is the best way to guarrantee the local environment for your application is identical between development and production (and you should use it). However, docker can&#x27;t guarrantee your DNS is configured correctly or that the VPN connection works properly. It can&#x27;t guarrantee your changes won&#x27;t bork the service orchestration nor service discovery, nor any of a thousand other things that &lt;em&gt;just couldn&#x27;t matter&lt;&#x2F;em&gt; but somehow do.&lt;&#x2F;p&gt;
&lt;p&gt;At least in AWS, different instance types can have radically different features. For example, an m3.medium will only run in a virtual private cloud which requires some serious networking knowledge, whereas a m1.medium works just fine in the stupidly-easy public cloud. A spot instance can disappear at any time or fail to launch if the spot price suddenly spikes, whereas an on-demand instance is guarranteed to boot up and stick around as long as you want it. These are just a few obvious differences. Make sure you use the same instance types to run tests against your feature branches and bug fixes that your production stack uses.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;production-traffic-real-data&quot;&gt;Production Traffic + Real Data&lt;&#x2F;h4&gt;
&lt;p&gt;Honestly, real data is most important with NoSQL databases where documents can be anything. If you&#x27;re using Postgres with a Rails App, this may be less important.&lt;&#x2F;p&gt;
&lt;p&gt;Duplicating production traffic is hard, but the kind of confidence it can give is unparallelled. You can instrument optimizations (especially around your database or cache) and &lt;em&gt;know&lt;&#x2F;em&gt; it will make your existing infrastructure more scalable without ending your startup. There&#x27;s just no other way to get those guarrantees.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;happy-sad-path-testing&quot;&gt;Happy + Sad Path Testing&lt;&#x2F;h4&gt;
&lt;p&gt;Testing failure scenarios is hard and counterintuitive. Which is why it causes so many production incidents. If there is a value to writing unit tests, I would say that seeing code coverage visually present all the code paths we missed is the highest one. Some testing frameworks (like rspec on its own) can make it feel hard to test lots of failure scenarios. &lt;&#x2F;p&gt;
&lt;p&gt;However, I&#x27;ve personally found that cucumber really makes it easy to create tons of integration tests. The &lt;code&gt;examples&lt;&#x2F;code&gt; and tables make it easy to add subtly different versions of the same test. The step definitions decouple specific tests from reusable code blocks in a super elegant fashion. Those and a million other things, make cucumber my new goto tool for less-painful integration testing.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;production-mode&quot;&gt;&amp;quot;Production&amp;quot; Mode&lt;&#x2F;h4&gt;
&lt;p&gt;This is just wrong:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;environment &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;== &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;production&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;) {
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Something untestable that will haunt your dreams
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;} &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; A sirensong luring you into false security
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Look, nothing interesting or important should &lt;strong&gt;ever&lt;&#x2F;strong&gt; happen in production that you haven&#x27;t properly tested before. If you hardcode specific environment detection, you by definition can&#x27;t test something interesting. This leads to surprising and harmful situations.&lt;&#x2F;p&gt;
&lt;p&gt;Where should this kind of setting go? Anywhere else:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Environment Variables&lt;&#x2F;li&gt;
&lt;li&gt;Config file&lt;&#x2F;li&gt;
&lt;li&gt;Database&lt;&#x2F;li&gt;
&lt;li&gt;Cloud-config user-data&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;ve seen several great examples of a &lt;code&gt;config&lt;&#x2F;code&gt; abstraction which can be queried for environment specific variables. So, something like this instead:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;databasePassword &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;databasePassword&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This code snippet is better than the conditional above because it guarrantees similarity between your local machine, your remote test environment, and your production server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;only-fast-tests&quot;&gt;Only Fast Tests&lt;&#x2F;h2&gt;
&lt;p&gt;If your integration tests take more than time than it takes to walk to the water cooler and back, your developers are going stop working and drift to hacker news for the next hour. Wait? What was I working on again...&lt;&#x2F;p&gt;
&lt;p&gt;Every integration test scenario should be able to run in any order so that you can parallelize the shit out of your tests. Write a lot of tests, but run them all at once across multiple machines&#x2F;containers&#x2F;vms&#x2F;whatever. Don&#x27;t settle for anything less than awesomely fast.&lt;&#x2F;p&gt;
&lt;p&gt;The secret to keeping your tests fast is to chip away at each subsequent bottleneck every sprint.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;deliver-immediately&quot;&gt;Deliver Immediately&lt;&#x2F;h2&gt;
&lt;p&gt;Why does it take so long to deploy changes as your cluster gets bigger and bigger? Because you&#x27;re rolling instances onesy-twosy. Rotating instances individually is slow and error prone. What happens if you get halfway through a deployment and one instance never comes back online? [Gulp.]&lt;&#x2F;p&gt;
&lt;p&gt;There is a right answer here. Bring up all new nodes at once while the old nodes are still running. The new instances will come online at basically the same time. When they do, split production between old and new nodes until your smoke tests pass. Then, drop the all the old instances at once. If some of your new nodes don&#x27;t come online or fail smoke tests, just abort the automated deployment and then manually back off production traffic. Your old nodes will still be humming along in the background.&lt;&#x2F;p&gt;
&lt;p&gt;This will take you from an O(n) deployment speed to a O(1) deployment speed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;deployment-pipelines-as-emergency-preparedness&quot;&gt;Deployment Pipelines as Emergency Preparedness&lt;&#x2F;h3&gt;
&lt;p&gt;Mistakes happen. Apps break. We can&#x27;t ever totally eradicate it, but having a fast deployment pipeline means that we can respond more quickly and minimize disruptions to our customers. Moreever, we can delight by spending more time developing fixes and features and less time waiting around.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;inspiring-deployment&quot;&gt;Inspiring Deployment&lt;&#x2F;h2&gt;
&lt;p&gt;Any time something &amp;quot;just works,&amp;quot; it&#x27;s truly a thing of beauty. If you&#x27;re a web developer, you need a fast reliable way to get your work into your customers hands. &lt;&#x2F;p&gt;
&lt;p&gt;Tell me your secrets for getting more out of your deployment pipeline, or tell me why I&#x27;m wrong above.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Testing AWS Go SDK Integrations</title>
		<published>2015-10-25T00:00:00+00:00</published>
		<updated>2015-10-25T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/testing-aws-go-sdk-integration/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/testing-aws-go-sdk-integration/</id>
		<content type="html">&lt;p&gt;When programming in Golang, the Mockery project painlessly generates Testify mocks from any interface. But very rarely (and specifically in the AWS Go SDK), you run into situations that Testify doesn&#x27;t handle well. If you think that Testify has failed you, then prepare to enter... &lt;em&gt;a minefield of horrors&lt;&#x2F;em&gt;!&lt;&#x2F;p&gt;
&lt;p class=&quot;responsive-image&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;andyandorla&#x2F;362709263&#x2F;&quot;&gt;
&lt;img alt=&quot;A sign labeled Minefield hangs on a forlorn barbed wire fence&quot; src=&quot;&#x2F;img&#x2F;2015&#x2F;minefield.jpg&quot;&gt;
&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-post-testify-minefield-of-horrors&quot;&gt;The Post-Testify Minefield of Horrors&lt;&#x2F;h2&gt;
&lt;p&gt;This &amp;quot;Post-Testify Minefield of Horrors&amp;quot; I speak of is a simple trilemma. You must decide between one of the following tradeoffs:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Don&#x27;t unit test this code&lt;&#x2F;strong&gt; -- what could go wrong?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Abandon Testify for this package&lt;&#x2F;strong&gt; -- hand write ALL the mocks!&lt;&#x2F;li&gt;
&lt;li&gt;Keep using Testify -- &lt;strong&gt;rewrite the mocks EVERY time your regenerate your mocks&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;None of these are great options. What if there was a way to use Testify mocks 99.99999% of the time, but handcraft &lt;em&gt;only&lt;&#x2F;em&gt; the special cases it doesn&#x27;t cover? That&#x27;s what I aim to offer you by the end of this post -- a way through this minefield.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;you-lie-testify-is-all-i-need&quot;&gt;You Lie! Testify Is All I Need&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2015&#x2F;throne-of-lies.gif&quot; alt=&quot;Elf says &#x27;You sit on a throne of lies!&#x27;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For AWS APIs which aren&#x27;t paginated, Testify works swimmingly, and Mockery does a fantastic job of writing all the tedious mocks for us.&lt;&#x2F;p&gt;
&lt;p&gt;However, as a prime example where Testify seems to break down, consider the DynamoDB package with the AWS Go SDK. Let&#x27;s say you want to query one of your DynamoDB tables, but the total amount of data you want to retrieve could become greater than 1 MB. According to the documentation:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you query or scan for specific attributes that match values that amount to more than 1 MB of data, you&#x27;ll need to perform another Query or Scan request for the next 1 MB of data.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;To ensure that your application retrieves &lt;em&gt;all&lt;&#x2F;em&gt; the query results, you need to call &lt;code&gt;DynamoDB.Query()&lt;&#x2F;code&gt; repeatedly with different inputs and outputs. That&#x27;s all very simple, but to unit test this code you&#x27;ll need a mock implementation of &lt;code&gt;DynamoDB.Query()&lt;&#x2F;code&gt; which can &lt;strong&gt;return different values on each call&lt;&#x2F;strong&gt;. Although Mockery shows some examples of this in their README, returning dynamic values seems not to work at time of writing (late October 2015).&lt;&#x2F;p&gt;
&lt;p&gt;So, this ideal solution I&#x27;ve promised you should both:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Use Testify mocks generated painlessly by Mockery for most of the AWS Go SDK&lt;&#x2F;li&gt;
&lt;li&gt;Allow us to handcraft select pieces of highly dynamic AWS Go SDK code&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So how can we achieve both?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-solution-separate-interfaces&quot;&gt;The Solution: Separate Interfaces&lt;&#x2F;h2&gt;
&lt;p class=&quot;responsive-image&quot;&gt;&lt;a href=&quot;http:&#x2F;&#x2F;imgur.com&#x2F;gallery&#x2F;wpont&quot;&gt;
&lt;img alt=&quot;The starship Enterprise separating the saucer from the ship&quot; src=&quot;&#x2F;img&#x2F;2015&#x2F;separate-starship.jpg&quot;&gt;
&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&lt;br&gt;
&lt;p&gt;We&#x27;ll write one interface that acts as a contract between our code and AWS Go SDK &lt;em&gt;in general&lt;&#x2F;em&gt;, and we&#x27;ll let Mockery generate mocks for this interface. Then, we write a very small dedicated interface for the handcrafted mocks. Any of the non-paginated AWS APIs we use in our code will go through the general interface, but our application will &lt;code&gt;Query()&lt;&#x2F;code&gt; only through the handcrafted interface. Since Mockery puts its generated code in a subdirectory &lt;code&gt;.&#x2F;mocks&lt;&#x2F;code&gt;, we can ensure our handcrafted mocks will survive by keeping them out of &lt;code&gt;.&#x2F;mocks&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-big-general-interface-interface&quot;&gt;The Big, General Interface Interface&lt;&#x2F;h3&gt;
&lt;p&gt;Since &lt;code&gt;DynamoDBer&lt;&#x2F;code&gt; stutters, let&#x27;s just call it &lt;code&gt;DBer&lt;&#x2F;code&gt;. The general interface &lt;code&gt;DBer&lt;&#x2F;code&gt; should look something like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;github.com&#x2F;aws&#x2F;aws-go-sdk&#x2F;services&#x2F;dynamodb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;DBer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;interface &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;GetItem &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;*argument*&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;*return values*&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;DeleteItem &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;*argument*&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;*return values*&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;UpdateItem &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;*argument*&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;*return values*&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;}

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;DBer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= (*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dynamodb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DynamoDB&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;nil&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then to call any method in AWS Go SDK, we always make sure that method has been added to &lt;code&gt;DBer&lt;&#x2F;code&gt; and use the &lt;code&gt;DBer&lt;&#x2F;code&gt; interface in our code, something like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;Get&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;id &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Item&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;db &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;DBer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dynamodb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;NewDynamoDB&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;error &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;db&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;GetItem&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;error
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Don&#x27;t try to compile that, but you get the gist. Now, to get all our mock objects for free, we&#x27;ll install Mockery. Then, we&#x27;ll cd into the code directory and run Mockery:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;get github&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;com&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;vektra&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;mockery
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;mockery &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;. -&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DBer
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At the end of the process, I&#x27;ll have a shiny new directoy &lt;code&gt;.&#x2F;mocks&lt;&#x2F;code&gt; containing all the Testify mocks for me. Sweet! Okay, that&#x27;s the big &lt;code&gt;DBer&lt;&#x2F;code&gt; interface. Let&#x27;s move on to the other interface.&lt;&#x2F;p&gt;
&lt;p class=&quot;responsive-image&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;evelinaa&#x2F;8507282095&#x2F;&quot;&gt;
&lt;img alt=&quot;A barber carefully shaves an enormous beard&quot; src=&quot;&#x2F;img&#x2F;2015&#x2F;handcrafted-beard.jpg&quot;&gt;
&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;&lt;br&gt;
&lt;h3 id=&quot;the-small-handcrafted-queryer-interface&quot;&gt;The Small, Handcrafted Queryer Interface&lt;&#x2F;h3&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;github.com&#x2F;aws&#x2F;aws-go-sdk&#x2F;services&#x2F;dynamodb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
)

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;Queryer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;interface &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;Query&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;QueryInput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) (*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;QueryOutput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
}

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Queryer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= (*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dynamodb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;DynamoDB&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;nil&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A &lt;code&gt;Queryer&lt;&#x2F;code&gt; only needs to implement the &lt;code&gt;Query()&lt;&#x2F;code&gt; method already in the DynamoDB package. For good measure, we create a nil and ignored DynamoDB struct object as a &lt;code&gt;Queryer&lt;&#x2F;code&gt; so that the compiler will verify that we have the method signature of &lt;code&gt;Queryer.Query()&lt;&#x2F;code&gt; matching &lt;code&gt;DynamoDB.Query()&lt;&#x2F;code&gt;. We want to make sure the compiler catches any obvious mistakes (like failing to satisfy an interface) before we ship this code into production. Otherwise, we might theoretically some day update the AWS Go SDK package, have the code compile, and ship this out into production only to discover failures too late.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wrapping-up-queryer-in-a-useful-func&quot;&gt;Wrapping Up Queryer in a Useful Func&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s assume your application should always retrieve all the results, we&#x27;ll want to create a wrapper method to DRY up and encapsulate all the logic necessary to retrieve all pages of results.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;paginatedQueryImpl&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;q &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Queryer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;QueryInput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) ([]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;}

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paginatedQuery &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paginatedQueryImpl
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s unpack this a bit. Why both a &lt;code&gt;paginatedQueryImpl()&lt;&#x2F;code&gt; and &lt;code&gt;paginatedQuery()&lt;&#x2F;code&gt;? The idea here is that we want to easily mock out the method that our application will actually use, so I&#x27;ve created a mutable variable &lt;code&gt;paginatedQueryImpl()&lt;&#x2F;code&gt;. This allows me to change the implementation on the fly in unit tests by just assigning a new lambda function.&lt;&#x2F;p&gt;
&lt;p&gt;However, for my unit tests on &lt;code&gt;paginatedQuery()&lt;&#x2F;code&gt; itself, I need to ensure that I&#x27;m testing the actual implementation. For that kind of assurance, I need to declare a function with the real implementation. Then, in my unit tests I can simply reassign the real implementation to my &lt;code&gt;paginatedQuery&lt;&#x2F;code&gt; variable as a setup step.&lt;&#x2F;p&gt;
&lt;p&gt;Then, I need a way to test the interaction between &lt;code&gt;paginatedQueryImpl&lt;&#x2F;code&gt; and DynamoDB, and that&#x27;s precisely why it takes a &lt;code&gt;Queryer&lt;&#x2F;code&gt; argument. This allows us to pass in either real &lt;code&gt;DynamoDB&lt;&#x2F;code&gt; objects or our handcrafted mocks. The handcrafted mock will simulate all the various test cases that we care about without needing to send any requests over the wire or even have an AWS account.&lt;&#x2F;p&gt;
&lt;p&gt;The implementation of &lt;code&gt;paginatedQuery()&lt;&#x2F;code&gt; is left as an exercise to the reader, since testing is the primary focus of this post.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;handcrafting-the-mock-queryer&quot;&gt;Handcrafting the Mock Queryer&lt;&#x2F;h3&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;github.com&#x2F;aws&#x2F;aws-go-sdk&#x2F;services&#x2F;dynamodb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
)

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;MockQueryer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;struct &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;QueryFunc &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;QueryInput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) (*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;QueryOutput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
}

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;m &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;MockQueryer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;Query&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;QueryInput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) (*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;QueryOutput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;m&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;QueryFunc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s a few salient points to discuss here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We &lt;em&gt;need&lt;&#x2F;em&gt; the &lt;code&gt;QueryFunc&lt;&#x2F;code&gt; field to be a lambda function, because we want to replace it with different implementations in each test case. We don&#x27;t want to define a different struct type for every test -- that&#x27;s just too much boilerplate code.&lt;&#x2F;li&gt;
&lt;li&gt;Because the &lt;code&gt;QueryFunc()&lt;&#x2F;code&gt; and &lt;code&gt;Query()&lt;&#x2F;code&gt; both have the same signature, we can simply passthrough the arguments and return values from the &lt;code&gt;Query()&lt;&#x2F;code&gt; to &lt;code&gt;QueryFunc()&lt;&#x2F;code&gt; and get compile-time checks that we defined our lambdas correctly in the tests.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;let-s-write-some-example-unit-tests&quot;&gt;Let&#x27;s Write Some Example Unit Tests&lt;&#x2F;h3&gt;
&lt;p&gt;The F# community&#x27;s writings about &lt;a href=&quot;http:&#x2F;&#x2F;fsharpforfunandprofit.com&#x2F;posts&#x2F;property-based-testing&#x2F;&quot;&gt;property-based testing&lt;&#x2F;a&gt; have had a profound impact on me. Whenever appropriate, I like to include some randomness in my tests to ensure that a whole range of similar but unpredictable values are tested to prevent an incomplete implementation from passing.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;math&#x2F;rand&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;github.com&#x2F;stretchr&#x2F;testify&#x2F;suite&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
)

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Define a Testify suite `MySuite` here and run it.

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;MySuite&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;TestPaginateQuery_NQueryResultPages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Testing&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;T&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;minPages &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;maxPages &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;50
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;itemsPerPage &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;totalPages &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;rand&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Int31n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;maxPages &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;minPages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) + &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;minPages
    totalItems &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;totalPages &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;itemsPerPage

    c &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;keys &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;rand&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Perm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;totalPages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;))
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;mq &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MockQueryer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;QueryFunc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;QueryInput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) (*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;QueryOutput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Run assertions on the arguments to QueryFunc()
          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;actual &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ExclusiveStartKey
          expected &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;keys&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;]
          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;c &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Equal&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ExclusiveStartKey&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;keys&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;])
          }

          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Generate the QueryOutput with correct LastKeyEvaluated
          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;output &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;makeQueryOutput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;itemsPerPage&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;totalPages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;++
          &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;output&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;nil
      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;}
    }
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paginatedQuery&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;mq&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dynamodb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;QueryInput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{})

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Len&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;totalItems&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Nil&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In our test method &lt;code&gt;TestPaginateQuery_NQueryResultPages&lt;&#x2F;code&gt;, the first five lines randomly pick how many pages of results and total items should exist when this test runs.&lt;&#x2F;p&gt;
&lt;p&gt;The next several lines of code create call counter &lt;code&gt;c&lt;&#x2F;code&gt; and create a new &lt;code&gt;MockQueryer&lt;&#x2F;code&gt; object &lt;code&gt;mq&lt;&#x2F;code&gt;. This object is passed into our the method we want to test &lt;code&gt;PaginateQuery()&lt;&#x2F;code&gt;. Now, we have complete control inside the QueryFunc method to calculate whatever return values we want AND to run assertions on the arguments to this method.&lt;&#x2F;p&gt;
&lt;p&gt;My test leaves a lot to be desired (and probably doesn&#x27;t even compile), but hopefully it can serve as an inspiration.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;testing-code-that-uses-paginatequery&quot;&gt;Testing Code that Uses PaginateQuery()&lt;&#x2F;h2&gt;
&lt;p&gt;I believe the clearest and easiest way to test code that invokes &lt;code&gt;paginateQuery()&lt;&#x2F;code&gt; is using the mutable&#x2F;immutable functions I&#x27;ve defined earlier. This allows us to stub replace the func during our tests and gives us unfettered access to control in the innerworkings of our code. Let&#x27;s see a short example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; retrieve_everything.go
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;RetrieveEverything&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() ([]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dynamo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dynamodb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;NewDynamoDB&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paginatedQuery&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dynamo&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;QueryInput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{})
}

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; retrieve_everything_test.go
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;MySuite&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;TestRetrieveEverytingPropagatesErrors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;paginatedQuery &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;Queryer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;QueryInput&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) ([]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;nil&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;errors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;New&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Dynamo Broke&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;)
    }
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RetrieveEverything&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;EqualError&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Dynamo Broke&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Nil&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In a similar fashion, we can replace &lt;code&gt;paginatedQuery()&lt;&#x2F;code&gt; with mocks that will return nil errors, improperly formatted data, a slice of results, an empty slice, or any other scenario that could conceivable happen.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;damn-your-smoke-and-mirrors-lambdas&quot;&gt;Damn Your Smoke and Mirrors Lambdas!&lt;&#x2F;h3&gt;
&lt;p&gt;The downside of this approach is that any part of your codebase could overwrite the func pointed to by &lt;code&gt;paginatedQuery()&lt;&#x2F;code&gt; which seems a bit unsafe. If any unit tests replace it with a mock, you&#x27;ll need to reset it to &lt;code&gt;paginatedQueryImpl()&lt;&#x2F;code&gt; as discussed earlier.&lt;&#x2F;p&gt;
&lt;p&gt;However if global mutable lambdas makes you uncomfortable, you can use Mockery to generate a mock for &lt;code&gt;DynamoDB.Query()&lt;&#x2F;code&gt; and indirectly control the output of &lt;code&gt;paginatedQuery()&lt;&#x2F;code&gt;. If you go that route, just don&#x27;t use a package variable. Instead, make &lt;code&gt;paginatedQuery&lt;&#x2F;code&gt; a proper immutable func in its own right. As far as I can tell, which strategy you use is entirely a matter of preference.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;taking-it-further&quot;&gt;Taking it Further&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;vektra&#x2F;mockery&quot;&gt;Mockery Project&lt;&#x2F;a&gt; is well worth a look if you haven&#x27;t seen it before.&lt;&#x2F;li&gt;
&lt;li&gt;Nearly everything I&#x27;ve said here applies equally to any other paginated function in the AWS Go SDK (and that&#x27;s a lot of functions). If you use this sort of technique a lot, it might make sense to write a code generator that can DRY up repeated logic and tests around this functions.&lt;&#x2F;li&gt;
&lt;li&gt;If you&#x27;re curious about DynamoDB, see the &lt;a href=&quot;https:&#x2F;&#x2F;docs.aws.amazon.com&#x2F;amazondynamodb&#x2F;latest&#x2F;developerguide&#x2F;QueryAndScan.html&quot;&gt;AWS documentation for Query&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Consider using AWS&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;docs.aws.amazon.com&#x2F;sdk-for-go&#x2F;api&#x2F;service&#x2F;dynamodb&#x2F;#DynamoDB.QueryPages&quot;&gt;DynamoDB QueryPages() method&lt;&#x2F;a&gt; instead of &lt;code&gt;Query()&lt;&#x2F;code&gt; in your own code since it handles lots of pagination messiness for you.&lt;&#x2F;li&gt;
&lt;li&gt;I omitted the definition of the &lt;code&gt;MySuite&lt;&#x2F;code&gt; struct in one of the examples above for brevity. See the &lt;a href=&quot;https:&#x2F;&#x2F;godoc.org&#x2F;github.com&#x2F;stretchr&#x2F;testify&#x2F;suite&quot;&gt;Testify suites godoc&lt;&#x2F;a&gt; if you need a refresher.&lt;&#x2F;li&gt;
&lt;li&gt;If you need to rerun just one test several times while you&#x27;re debugging or writing tests, use &lt;code&gt;go test .&#x2F;path&#x2F;to&#x2F;package -run TestFunc&lt;&#x2F;code&gt; where &amp;quot;TestFunc&amp;quot; is the name of the test that go knows how to run (ex: the func that starts your Testify suite).&lt;&#x2F;li&gt;
&lt;li&gt;For integration tests, I recommend trying AWS&#x27;s &lt;a href=&quot;http:&#x2F;&#x2F;docs.aws.amazon.com&#x2F;amazondynamodb&#x2F;latest&#x2F;developerguide&#x2F;Tools.DynamoDBLocal.html&quot;&gt;local-dynamo jar&lt;&#x2F;a&gt;. It seems to be maintained and reasonably performant as a drop-in replacement for dynamodb that runs on your own machine or CI build environment.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Golang is a fantastic language for writing unit tests, but it sometimes requires some creativity to test all the code paths you care about. Consider using smaller interfaces to mock out isolated complex dependencies that Testify can&#x27;t handle well.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Special Thanks to Julian Cooper for all the ideas about lambdas and small interfaces presented here, to Phil Cluff for allowing me to write about my work, and my wife Emilie Fisher-Fleig for the insightful comments on my drafts.&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Effective Commit Messages</title>
		<published>2015-05-25T00:00:00+00:00</published>
		<updated>2015-05-25T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/effective-commit-messages/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/effective-commit-messages/</id>
		<content type="html">&lt;p&gt;There are a variety of ways to include documentation in your project. I want to
focus on commit messages as a vital clue in debugging and refactoring your code.&lt;&#x2F;p&gt;
&lt;p&gt;Although I use git exclusively at this point, similar techniques will work for
mercurial (and svn to a lesser extent).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;strengths-of-commit-messages&quot;&gt;Strengths of Commit Messages&lt;&#x2F;h2&gt;
&lt;p&gt;The great thing about using git commit messages is that &lt;em&gt;they persist exactly the
same length of time as the code they describe&lt;&#x2F;em&gt;. This is a huge advantage compared
to comments in the source code. When that code changes or disappears, so do the
commit messages. Using commit messages as documentation ensures &lt;em&gt;always&lt;&#x2F;em&gt; up to date
documentation.&lt;&#x2F;p&gt;
&lt;p&gt;A commit has a timestamp and an author associated with it. Additionally, a commit
has a position in git history. For teams adopting Github (or similar services),
you can often look forward from a particular commit in git history to determine
which pull request and agile story this line of code came from. You can examine
adjacent commits for extra clues about related changes. If you get really confused
by a message, you know exactly who wrote that message and you can talk to that person
if they still work at your company.&lt;&#x2F;p&gt;
&lt;p&gt;In git and mercurial, a commit preserves a context, because it is simply a patch
of the previous commit. You can see all the other changes that happened at the
same time, which can help ask that elusive &lt;em&gt;why&lt;&#x2F;em&gt; question: &amp;quot;Why was this code written?&amp;quot;
Often, simply reading through the other code written at the same time helps me
wrap understand a particular line of code.&lt;&#x2F;p&gt;
&lt;p&gt;You don&#x27;t have to adopt any new software, database, or programming philosophy.
If you&#x27;re using version control software already, this information is already there.
Whether or not you leverage all of this metadata is up to you.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;comparison-to-source-comments&quot;&gt;Comparison to Source Comments&lt;&#x2F;h2&gt;
&lt;p&gt;The problem with comments is that they can remain in the codebase a long time
after the code they originally described is removed or altered. This means that
the context can be ambiguous. A related problem is that changes to the code are
not always accompanied with changes to the comments, leading to inaccurate or out
of date information.&lt;&#x2F;p&gt;
&lt;p&gt;The main strength of comments versus commit messages is that comments are visible
to readers of the source code.&lt;&#x2F;p&gt;
&lt;p&gt;In my opinion, comments are best for situations in which there is some hack that is
&lt;em&gt;really&lt;&#x2F;em&gt; important to understand and preserve. Any time I include a hack in source
code because it seemed necessary, I leave a comment in the source code. Serious
gotchas also deserve to be included in the source code directly as  well.&lt;&#x2F;p&gt;
&lt;p&gt;However, like any code, comments themselves add a maintenance burden. I tend to
use commit messages anywhere I can.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;retrieving-a-commit-message&quot;&gt;Retrieving a Commit Message&lt;&#x2F;h2&gt;
&lt;p&gt;Commit messages are harder to find than comments. Let&#x27;s look at how to find the
commit that a line of code belongs to.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;in-atom&quot;&gt;In Atom&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2015&#x2F;atom-git-blame.png&quot; style=&quot;max-width:100%; height:auto !important;&quot;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This screenshot shows using the &lt;a href=&quot;https:&#x2F;&#x2F;atom.io&#x2F;packages&#x2F;git-blame&quot;&gt;git-blame package&lt;&#x2F;a&gt;
for Atom.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;in-eclipse&quot;&gt;In Eclipse&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2015&#x2F;eclipse-git-blame.jpg&quot; style=&quot;max-width:100%; height:auto !important;&quot;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This screenshot comes from &lt;a href=&quot;http:&#x2F;&#x2F;www.eclipse.org&#x2F;egit&#x2F;&quot;&gt;EclipseGit package&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;in-sublime&quot;&gt;In Sublime&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2015&#x2F;sublime-git-blame.png&quot; style=&quot;max-width:100%; height:auto !important;&quot;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This image comes from an &lt;a href=&quot;http:&#x2F;&#x2F;www.sublimetext.com&#x2F;forum&#x2F;viewtopic.php?f=5&amp;amp;t=2172&quot;&gt;ancient forum post&lt;&#x2F;a&gt;,
but it gives &lt;em&gt;some&lt;&#x2F;em&gt; idea of what to expect.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;in-the-shell&quot;&gt;In the Shell&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img alt=&quot;Animation showing git blame and git show together&quot; src=&quot;&#x2F;img&#x2F;2015&#x2F;git-blame-show.gif&quot; style=&quot;max-width:100%; height:auto !important;&quot;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Use &lt;a href=&quot;http:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;git-blame&quot;&gt;&lt;code&gt;git blame&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; git blame&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -L22&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,25 src&#x2F;model&#x2F;resource.js
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;-L&lt;&#x2F;em&gt; stands for lines, in this case lines 22 to 25&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;src&#x2F;model&#x2F;resource.js&lt;&#x2F;em&gt; is the specific file you want to look into&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This code displays the shortened commit hash next to the contents of those lines
from the file. Mercurial has a nearly identical command &lt;code&gt;hg blame&lt;&#x2F;code&gt;.  To
actually see the corresponding log message, use:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; git show abcde12345
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;where &lt;code&gt;abcde12345&lt;&#x2F;code&gt; is the actual commit hash from git blame.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;If you know of a way to get the commit message from a specific file and line number in a single
command, please tell me in the comments!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;writing-the-commit-message-title&quot;&gt;Writing the Commit Message Title&lt;&#x2F;h2&gt;
&lt;p&gt;If you use Github, you&#x27;ll also see that Github will use the first 50 characters
of the first line of your commit message as a heading for that commit (the second
line needs to be blank):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img alt=&quot;Heading on Github for commit&quot; src=&quot;&#x2F;img&#x2F;2015&#x2F;commit-first-line-heading.png&quot; style=&quot;max-width:100%; height:auto !important;&quot;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;...or as the title for a single commit PR:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img alt=&quot;Pull Request title&quot; src=&quot;&#x2F;img&#x2F;2015&#x2F;github-first-line-pr-title.png&quot; style=&quot;max-width:100%; height:auto !important;&quot;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you use vim to edit your commit message, the first 50 characters are one color,
and anything beyond 50 will turn another color. Using that visual clue helps you communicate
the purpose of a commit to the rest of the team at a glance.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img alt=&quot;VIm commit message visual clue&quot; src=&quot;&#x2F;img&#x2F;2015&#x2F;vim-first-line-visual-clue.png&quot; style=&quot;max-width:100%; height:auto !important;&quot;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;However, if you ignore this 50 character cutoff, commmit messages will be cut up
and difficult to read, like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2015&#x2F;git-commit-first-line-too-long.png&quot; style=&quot;max-width:100%; height:auto !important;&quot;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;categorizing-the-commit&quot;&gt;Categorizing the Commit&lt;&#x2F;h3&gt;
&lt;p&gt;Another helpful convention I sometimes follow is starting the commit title with
the kind of change I&#x27;m making:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;(fix) - no new feature, simply a fix for a bug in existing code&lt;&#x2F;li&gt;
&lt;li&gt;(feat) - new functionality introduced, possibly tests and documentation as well&lt;&#x2F;li&gt;
&lt;li&gt;(refactor) - reimplements internals&lt;&#x2F;li&gt;
&lt;li&gt;(doc) - updates to a README or comments&lt;&#x2F;li&gt;
&lt;li&gt;(test) - adding or fixing a test&lt;&#x2F;li&gt;
&lt;li&gt;(nit) - changes to whitespace, code style, or something else super trivial&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;helpful-commit-message-bodies&quot;&gt;Helpful Commit Message Bodies&lt;&#x2F;h2&gt;
&lt;p&gt;I like to use everything after the second line of a commit message to describe:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;what problem each commit is addressing&lt;&#x2F;li&gt;
&lt;li&gt;what subtle gotchas are lurking beneath the surface&lt;&#x2F;li&gt;
&lt;li&gt;why I choose this implementation over another way of writing the code&lt;&#x2F;li&gt;
&lt;li&gt;the issue number for later cross-referencing&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I often use &lt;a href=&quot;http:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;git-bisect&quot;&gt;&lt;code&gt;git bisect&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to debug tricky but
reproducible errors, and I like to think of commit messages as clues to my future
self during a bug-hunt six months from now. But &lt;code&gt;git bisect&lt;&#x2F;code&gt; is a subject for another post.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;example-commit-message-body&quot;&gt;Example Commit Message Body&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
  &lt;p&gt;change all app.user.getUploadedFilesData() to get files metadata to asynchronous operations&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Not great -- why did we change to asynchronous operations? Also, this message is
too long for github. It will be seen in PR titles or commit summaries as
&lt;code&gt;change all app.user.getUploadedFilesData() to g...&lt;&#x2F;code&gt;. That tells me almost
nothing about what change happened or why! Let&#x27;s see if we can improve things:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
  &lt;p&gt;retrieve user uploads from db for persistence&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This explains, at a glance, &lt;em&gt;why&lt;&#x2F;em&gt; this commit was made. Uploaded files are stored
in the database now, why? For better persistence. Okay, so that&#x27;s really important,
and it provides me a lot more clues about the bug I&#x27;m trying to fix. But this
doesn&#x27;t mention anything about asynchronous operations... how can we fit that onto
the first line?&lt;&#x2F;p&gt;
&lt;p&gt;We can&#x27;t. But we can provide more in-depth discussion of the details of the changes
in lines 3 and later in the commit message. Let&#x27;s take a look:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
  &lt;p&gt;retrieve user uploads from db for persistence&lt;&#x2F;p&gt;
  &lt;p&gt;Because this operation requires db access, all calls to app.user.getUploadedFilesData()
     are now asynchronous calls. We had to start using the database because all
     metadata (like filesize) was lost each time we restart the app.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Better, but why didn&#x27;t the author just grab this info from the file system? Let&#x27;s
try one more improvement:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
  &lt;p&gt;(feat) retrieve user uploads from db for persistence&lt;&#x2F;p&gt;
  &lt;p&gt;Because this operation requires db access, all calls to app.user.getUploadedFilesData()
     are now asynchronous calls. We had to start using the database because all
     metadata (like filesize) was lost each time we restart the app.&lt;&#x2F;p&gt;
  &lt;p&gt;Since we use clusters of machines, not all machines in the cluster have all
     files all the time. We use the database so that whichever machine in the
     cluster handles the request can access the file data.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Perfect! We have a one line summary in github, we have a detailed explanation of
why app.user.getUploadedFilesData() is now async, and we know why some easier
option wasn&#x27;t used. There&#x27;s probably still plenty of room for improvement, but
this is a great foundation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;example-trivial-commit-message&quot;&gt;Example Trivial Commit Message&lt;&#x2F;h3&gt;
&lt;p&gt;Not all commits need extended explanations. Let&#x27;s look an example for a
change that&#x27;s relatively trivial:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
  &lt;p&gt;fix colors&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That gives us some idea of what&#x27;s happening, but it doesn&#x27;t answer the &lt;em&gt;why&lt;&#x2F;em&gt;
question. Let&#x27;s see if we can improve this message a bit:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
  &lt;p&gt;matched header color to brand guide&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Much better! Now we can tell at a glance &lt;em&gt;why&lt;&#x2F;em&gt; this change was made and which
kinds of colors were changed. But we still don&#x27;t know where to find more info
in our issue tracker. Let&#x27;s make another change:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
  &lt;p&gt;(nit) matched header color to brand guide [203]&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is a great message. We can go to issue 203 if we ever need to learn more
about who wanted this work done, or why this shade of green was chosen instead
of the actual shade specified in the brand guide.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;Commit messages &lt;em&gt;can&lt;&#x2F;em&gt; be a superior form of documentation for a team that adopts
them. There&#x27;s nothing new or extra you need to do to take advantage of source
control history today, but there&#x27;s a lot of advantages to carefully describing
the work you do in your commit messages.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Special Thanks to &lt;a href=&quot;https:&#x2F;&#x2F;www.300feetout.com&#x2F;teammember&#x2F;rex-vokey&#x2F;&quot;&gt;Rex Vokey&lt;&#x2F;a&gt; for suggesting more images,
&lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;jeffballardfuzzywaffles&quot;&gt;Jeff Ballard&lt;&#x2F;a&gt; for broadening my horizons,
&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;blainsadler&quot;&gt;Blain Sadler&lt;&#x2F;a&gt; for support,
&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;matt_mcclure&quot;&gt;Matt McClure&lt;&#x2F;a&gt; for the inspiration,
and my stunning wife for providing extensive feedback on this article.&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Setting up Istanbul with Jasmine on NodeJS</title>
		<published>2015-04-17T00:00:00+00:00</published>
		<updated>2015-04-17T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/setting-up-istanbul-with-jasmine/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/setting-up-istanbul-with-jasmine/</id>
		<content type="html">&lt;p&gt;Istanbul is perhaps the best test coverage tool for NodeJS. Istanbul&#x27;s metrics include lines of code, functions, and branches; and it generates reports that color each line to indicate full, partial, or no coverage. I also love that the reports break down test coverage per directory and file, which I use to identify blind spots.&lt;&#x2F;p&gt;
&lt;p&gt;While Istanbul can work with any testing framework, I focus on NodeJS and Jasmine, but much of the background information is relevant for other configurations.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-istanbul-works&quot;&gt;How Istanbul Works&lt;&#x2F;h2&gt;
&lt;p&gt;Istanbul is &lt;strong&gt;not&lt;&#x2F;strong&gt; a static code analysis tool, but rather it observes running code. It works in two distinct steps which are important to understand:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Instrument the code&lt;&#x2F;li&gt;
&lt;li&gt;Run the tests against the new code&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;instrument-the-code&quot;&gt;Instrument the Code&lt;&#x2F;h3&gt;
&lt;p&gt;By &amp;quot;instrument&amp;quot;, we mean create a functioning mock of the source code, while still invoking the real implementation. It&#x27;s much like using Jasmine&#x27;s &lt;code&gt;spyOn().andCallThrough()&lt;&#x2F;code&gt;. Effectively Istanbul is able to check whether or a given unit of code has executed, much like using Jasmine&#x27;s &lt;code&gt;expect(spy).toHaveBeenCalled()&lt;&#x2F;code&gt;. The main difference is that the instrumented code will become files rather than in memory objects.&lt;&#x2F;p&gt;
&lt;p&gt;The configuration for Istanbul must point out which files to instrument. Perhaps the biggest gotcha here is instrumenting the tests. You don&#x27;t want to do that for reasons that will become clear later on. The main thing to understand is that Istanbul needs to spy on your application, not your tests.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;run-the-tests&quot;&gt;Run the Tests&lt;&#x2F;h3&gt;
&lt;p&gt;Now that you have instrumented code, have Jasmine run it&#x27;s tests &lt;em&gt;against the instrumented code.&lt;&#x2F;em&gt; This means that Istanbul can determine exactly which units of code executed during the test run. Our configuration will also need to specify how to run the tests.&lt;&#x2F;p&gt;
&lt;p&gt;Once the test run is finished, Istanbul will output all the reports and display some summary metrics in the console. Configuration directs Istanbul to use one of several formats for the reports.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;show-me-the-code&quot;&gt;Show Me The Code!&lt;&#x2F;h2&gt;
&lt;p&gt;Enough chitter-chatter. Here&#x27;s how I setup things in my project:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; cd &#x2F;path&#x2F;to&#x2F;project
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; npm install&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --save&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; istanbul jasmine-node
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; mkdir&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Istanbul and Jasmine-node are built as command line tools, thus normally they would be globally installed. Notice I&#x27;m not doing that. In my experience, global packages are fundamentally wrong headed and are the biggest disparity between dev and production environments. Fortunately, npm makes using non-global command tools easy. Add something like the following to your package.json file:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: {
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;istanbul cover --include-all-sources jasmine-node test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
  }
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Put all your tests for jasmine-node into the test directory. To run your test suite AND get test coverage do this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; npm test
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Running &lt;code&gt;npm test&lt;&#x2F;code&gt; is really a shortcut for &lt;code&gt;npm run test&lt;&#x2F;code&gt;, which is the &lt;code&gt;test&lt;&#x2F;code&gt; command we created in package.json. Although the &lt;code&gt;istanbul&lt;&#x2F;code&gt; executable isn&#x27;t normally available in the shell (because we didn&#x27;t install it globally), npm makes it available inside the scripts listed in package.json. None of this should be suprising -- it&#x27;s not specific to istanbul.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s dig into the istanbul specific bits a little more:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;--include-all-sources&lt;&#x2F;code&gt; flag causes istanbul to recurse through directories for more source files to instrument&lt;&#x2F;li&gt;
&lt;li&gt;everything after &lt;code&gt;istanbul cover [flags]&lt;&#x2F;code&gt; is the test command (in this case &lt;code&gt;jasmine-node test&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;by default, istanbul ignores the &lt;code&gt;test&lt;&#x2F;code&gt; and &lt;code&gt;tests&lt;&#x2F;code&gt; directory. If we had used &lt;code&gt;spec&lt;&#x2F;code&gt; instead, we&#x27;d have to configure istanbul to ignore that directory&lt;&#x2F;li&gt;
&lt;li&gt;by default, istanbul creates reports in a directory &lt;code&gt;coverage&lt;&#x2F;code&gt; under the current working directory&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There&#x27;s a lot more configuration options which I&#x27;m not going into here. If you want to explore them, try this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;cd&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; &#x2F;path&#x2F;to&#x2F;project
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;.&#x2F;node_modules&#x2F;.bin&#x2F;istanbul&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; help config
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;design-considerations&quot;&gt;Design Considerations&lt;&#x2F;h2&gt;
&lt;p&gt;There&#x27;s lots of choices of you can make while setting up Istanbul, and a few problems to avoid.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;git-ignore-coverage-reports&quot;&gt;Git Ignore Coverage Reports&lt;&#x2F;h3&gt;
&lt;p&gt;The coverage reports are generated from your source, but you don&#x27;t want to ship that into production. Add that directory to your gitingore.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;gotcha-instrumenting-the-tests&quot;&gt;Gotcha: Instrumenting the Tests&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s revisit the gotcha mentioned above -- accidentally instrumenting your tests. If only your tests are instrumented, Istanbul will report 100% coverage. You&#x27;ll slap yourself on the back -- 100%! We did it! Then you&#x27;ll realize something&#x27;s off. If you instrument your source code along with the tests, all metrics will skew upwards proportional to size of your tests.&lt;&#x2F;p&gt;
&lt;p&gt;Not to be a broken record, here, but Istanbul is mocking out your entire application to observe what your tests are covering. If you accidentally have Istanbul mock out your tests, Istanbul will notice that all of your tests have run, which is not very helpful.&lt;&#x2F;p&gt;
&lt;p&gt;To help keep tests and source code distinct for Istanbul, I recommend using a separate top level directory for tests and source code in your project.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;running-the-tests-once-or-twice&quot;&gt;Running the Tests Once or Twice&lt;&#x2F;h3&gt;
&lt;p&gt;In my limited experience, running Jasmine against the instrumented code produces the same results as running Jasmine against the original source code. If Jasmine fails the test run, its process returns a non-zero exit code. Istanbul&#x27;s process then passes through a non-zero exit code as well if Jasmine fails. Thus, however you run your tests (npm, grunt, gulp, Circle CI, Travis, etc), you can  run the Jasmine tests once and get the results faster.&lt;&#x2F;p&gt;
&lt;p&gt;If you don&#x27;t trust the results of the instrumented code, the other choice would be to run the Jasmine tests twice -- once against the source code and once against the instrumented code.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;one-or-many-coverages&quot;&gt;One or Many Coverages&lt;&#x2F;h3&gt;
&lt;p&gt;Not all tests are created equal. You might want to have integration tests as well as unit tests. Think hard about to categorize each kind of test you have (or want to have). You might consider having Istanbul check the unit test coverage separately from the integration test. Here&#x27;s one way you might do that:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;scripts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: {
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;npm run unit &amp;amp;&amp;amp; npm run integration&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;unit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;istanbul cover --include-all-sources jasmine-node test&#x2F;unit&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;integration&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;istanbul cover --include-all-sources jasmine-node test&#x2F;integration&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
  }
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;While it could be helpful to setup separate coverage checks, you won&#x27;t have insight into the overall test coverage on the codebase as a whole. If you do decide to break test coverage into separate catgories, I also recommend having an overall coverage report generated.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Handlebars Considered Harmful</title>
		<published>2015-03-02T00:00:00+00:00</published>
		<updated>2015-03-02T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/handlebars-considered-harmful/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/handlebars-considered-harmful/</id>
		<content type="html">&lt;p&gt;&lt;strong&gt;TL;DR:&lt;&#x2F;strong&gt; Handlebars is a DSL builder in disguise tempting you to create an
undebuggable mess. If you minimize helpers and keep &lt;em&gt;all&lt;&#x2F;em&gt; procedural code outside handlebars, you can safely keep it in your toolbelt.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;do-try-this-at-home-kids&quot;&gt;DO try this at home, kids&lt;&#x2F;h2&gt;
&lt;p&gt;When I wrote this blog post back in 2014, I built a stupidly simple and deliberately buggy handlebars node app for this blog post. In the intervening years, I deleted this messy code on accident. All the error messages and code samples come directly from this repo.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;it-all-started-so-innocently&quot;&gt;It all started so innocently...&lt;&#x2F;h2&gt;
&lt;p&gt;You&#x27;re building a nodejs web application to track fitness, so of course you&#x27;re going to use handlebars. It&#x27;s what we nodejs developers do. Like all good startups, you move fast and break things. &amp;quot;Red, green, refactor&amp;quot; is your freaking middle name. The code you write may not be poetry, but you make shit happen.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;until-everything-went-terribly-wrong&quot;&gt;...Until everything went terribly wrong&lt;&#x2F;h2&gt;
&lt;p&gt;One day, you receive an urgent bug report from the CEO that there&#x27;s an icky error message every time he logs in. After verifying this issue doesn&#x27;t happen &lt;a href=&quot;http:&#x2F;&#x2F;localhost:3000&#x2F;?user=bff&quot;&gt;for your user&lt;&#x2F;a&gt;, you can reproduce this problem by running the source code on your machine and trying to &lt;a href=&quot;http:&#x2F;&#x2F;localhost:3000&#x2F;?user=wycats&quot;&gt;login as the CEO&lt;&#x2F;a&gt;. Here&#x27;s the error:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;TypeError:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; Cannot read property &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;activityStyle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; of undefined
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;at&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; Handlebars.registerHelper.(anonymous function) (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;&#x2F;var&#x2F;app&#x2F;helpers.js:45:47&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;at&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; Object.eval (eval at &amp;lt;anonymous&amp;gt; (&#x2F;var&#x2F;app&#x2F;node_modules&#x2F;handlebars&#x2F;dist&#x2F;cjs&#x2F;handlebars&#x2F;compiler&#x2F;javascript-compiler.js:209:23)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;anonymous&amp;gt;:5:125)
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;at&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; Object.ret (&#x2F;var&#x2F;app&#x2F;node_modules&#x2F;handlebars&#x2F;dist&#x2F;cjs&#x2F;handlebars&#x2F;runtime.js:144:30)
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;at&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; Object.ret &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;as summaryActivity&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; (&#x2F;var&#x2F;app&#x2F;node_modules&#x2F;handlebars&#x2F;dist&#x2F;cjs&#x2F;handlebars&#x2F;compiler&#x2F;compiler.js:462:21)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;at&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; null.&amp;lt;anonymous&amp;gt; (&#x2F;var&#x2F;app&#x2F;helpers.js:17:52)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;at&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; Object.eval (eval at &amp;lt;anonymous&amp;gt; (&#x2F;var&#x2F;app&#x2F;node_modules&#x2F;handlebars&#x2F;dist&#x2F;cjs&#x2F;handlebars&#x2F;compiler&#x2F;javascript-compiler.js:209:23)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;anonymous&amp;gt;:9:76)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;at&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; Object.prog &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;as fn&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; (&#x2F;var&#x2F;app&#x2F;node_modules&#x2F;handlebars&#x2F;dist&#x2F;cjs&#x2F;handlebars&#x2F;runtime.js:178:15)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;at&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; Handlebars.registerHelper.url (&#x2F;var&#x2F;app&#x2F;helpers.js:22:17)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;at&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; Object.eval (eval at &amp;lt;anonymous&amp;gt; (&#x2F;var&#x2F;app&#x2F;node_modules&#x2F;handlebars&#x2F;dist&#x2F;cjs&#x2F;handlebars&#x2F;compiler&#x2F;javascript-compiler.js:209:23)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;anonymous&amp;gt;:4:83)
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;at&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; Object.prog &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;as fn&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; (&#x2F;var&#x2F;app&#x2F;node_modules&#x2F;handlebars&#x2F;dist&#x2F;cjs&#x2F;handlebars&#x2F;runtime.js:178:15)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;a-useless-stack-trace&quot;&gt;A Useless Stack Trace&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;em&gt;only&lt;&#x2F;em&gt; connection between the stack trace above and our code is that first line. Every other line in the stack trace is from Handlebars&#x27; compiler or runtime &lt;code&gt;eval&lt;&#x2F;code&gt;ing anonymous functions. We have no idea what the context was that triggered the error in helpers.js. Hopefully it&#x27;s just an obvious syntax error in helper.js line 45.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;It&#x27;s interesting files names for a template package though, right? Weirdly, &amp;quot;compiler&amp;quot; and &amp;quot;runtime&amp;quot; remind me of something awfully familiar from my computer science classes...&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Enough daydreaming, let&#x27;s see what&#x27;s happening in helpers.js:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Handlebars&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;registerHelper&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;setActivityStyleFromUser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;activityStyle &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;== &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;null &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;prefs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;activityStyle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;activityStyle &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;prefs&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;activityStyle&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
  }
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;options&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);
});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Damn! There&#x27;s 2 usages of &lt;code&gt;activityStyle&lt;&#x2F;code&gt; on line 45 that look a bit suspect. Never fear -- we have the &lt;code&gt;{% raw %}{{log}}{% endraw %}&lt;&#x2F;code&gt; helper to show use what&#x27;s going on.&lt;&#x2F;p&gt;
&lt;p&gt;Phew! In the console, we see that the &lt;code&gt;user.prefs.activityStyle&lt;&#x2F;code&gt; is what&#x27;s giving us trouble.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-plethora-of-confusing-grep-results&quot;&gt;A plethora of confusing grep results&lt;&#x2F;h3&gt;
&lt;p&gt;But, how did we get here? Why is only the CEO crashing on login? If we rewrite this helper, what might we break elsewhere?&lt;&#x2F;p&gt;
&lt;p&gt;Time to search through all the code with grep to find where &lt;code&gt;setActivityStyleFromUser&lt;&#x2F;code&gt; is being used. Hopefully there&#x27;s only one code path to this particular helper; otherwise, it&#x27;s going to be a long night.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash{% raw %}&quot; data-lang=&quot;bash{% raw %}&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;bryce@server:&#x2F;var&#x2F;app $ grep -ir setActivityStyleFromUser .
.&#x2F;helpers.js:Handlebars.registerHelper(&amp;#39;setActivityStyleFromUser&amp;#39;, function(user, options) {
.&#x2F;components&#x2F;burnIt.hbs:      {{#setActivityStyleFromUser user}}
.&#x2F;components&#x2F;burnIt.hbs:      {{&#x2F;setActivityStyleFromUser}}
.&#x2F;components&#x2F;summaryActivity.hbs:  {{#setActivityStyleFromUser user}}
.&#x2F;components&#x2F;summaryActivity.hbs:  {{&#x2F;setActivityStyleFromUser}}
{% endraw %}```

We&amp;#39;ve read through that first result, where the handlebar helper is being registered, so we can ignore that one. Both burnIt.hbs and summmaryActivity.hbs are possible places this is being used. Double damn! Two code paths possibly leading to the original error message, but we don&amp;#39;t know which one is giving us trouble for sure. Let&amp;#39;s look at burnIt.hbs:

```handlebars{% raw %}
{{#setAsUser (getUsernameFromParams url=..&#x2F;urlParams)}}
  {{#setActivityStyleFromUser user}}
    &amp;lt;h1&amp;gt;Burn those {{activityStyle}}s, {{user.name}}!&amp;lt;&#x2F;h1&amp;gt;
  {{&#x2F;setActivityStyleFromUser}}
{{&#x2F;setAsUser}}
{% endraw %}```

Hmm... let&amp;#39;s try logging `activityStyle` just above `{% raw %}{{#setActivityStyleFromUser}}{% endraw %}`. So the code will now look like this:

```handlebars{% raw %}
{{#setAsUser (getUsernameFromParams url=..&#x2F;urlParams)}}
  {{log activityStyle}}
  {{#setActivityStyleFromUser user}}
    &amp;lt;h1&amp;gt;Burn those {{activityStyle}}s, {{user.name}}!&amp;lt;&#x2F;h1&amp;gt;
  {{&#x2F;setActivityStyleFromUser}}
{{&#x2F;setAsUser}}
{% endraw %}```

No change! We still get the same error message on the same line.

### Time to call a friend

We could try rooting around like this forever -- but let&amp;#39;s not. DuckDuckGo to the rescure:

![Several DuckDuckGo search results -- none look promising](&#x2F;img&#x2F;2015&#x2F;ddg-search-results.png)

Ugh, too many results and after clicking through several of them, there&amp;#39;s nothing useful. Looking directly on stackoverflow [doesn&amp;#39;t yield anything helpful](http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;25555793&#x2F;typeerror-cannot-read-property-typekey-of-undefined) here either.

### Why is everything so f**ked up?

This question is an opportunity. It&amp;#39;s a chance to take stock of what we&amp;#39;ve just gone through and ask ourselves why. Why is hunting this bug so hard? There&amp;#39;s barely any code written in this project, and already:

 + The templates look like a transcript of the delusional ramblings of a crazy person
 + The error messages are really long but tell us nothing
 + The control flow is untraceable

Can we figure it out? Maybe. **But if we keep writing code this way, someday it will paralyze our ability to fix bugs or develop new features**, and in my book that&amp;#39;s harmful.

## The Truth about Handlebars

The core of [handlebars is written in Bison&#x2F;YACC](https:&#x2F;&#x2F;github.com&#x2F;wycats&#x2F;handlebars.js&#x2F;blob&#x2F;master&#x2F;src&#x2F;handlebars.yy), a metalanguage used to create new programming languages like PHP, Ruby, Go, Bash, PostgreSQL. Handlebars itself is a very minimal Domain Specific Language which is why you saw &amp;quot;compiler.js&amp;quot; and &amp;quot;runtime.js&amp;quot; listed in the stacktrace at the top of this post. While implemented in javascript, code written in handlebars is running in its own compiler and runtime, on top of javascript.

Handlebars is extensible through the use of helpers that you register with the compiler. Think of helpers not like functions in javascript, but more like macros in C++. You&amp;#39;re literally adding capabilities to the compiler. So, as you write additional helpers for Handlebars, you&amp;#39;re _effectively_ extending Handlebars into your own DSL. Surprise!

In my stupid source code used to write this post, you can start to see this happening in the helpers I created:

```javascript{% raw %}
Handlebars.registerHelper(&amp;#39;eq&amp;#39;, function(a, b, options) {
  if (a == b) {
    return options.fn(this);
  } else {
    return options.inverse(this);
  }
});

Handlebars.registerHelper(&amp;#39;set&amp;#39;, function(key, value) {
  this[key] = value;
  return &amp;#39;&amp;#39;;
});
{% endraw %}```

I&amp;#39;ve re-implemented conditionals and variable assignment operators! Does that sound like the purview of templates to you? It doesn&amp;#39;t to me.

## But I&amp;#39;m not just picking on Handlebars

I&amp;#39;ve seen senior Python developers do exactly the same kind of thing using [Jinja2](http:&#x2F;&#x2F;jinja.pocoo.org&#x2F;) or even Django templates. If I had done any Ruby on Rails development, I&amp;#39;m quite sure I would find the same defects there as well.

PHP is perhaps the worst offender in this regard because it was explicitly developed to be embedded into html between `&amp;lt;?php` and `?&amp;gt;` tags. WordPress templates in particular suffer from the comingling of application logic and presentational markup leading to total spaghetti code. At least the debugging tools for PHP were built with this comingling in mind. Except that PHP now has [Twig templates](http:&#x2F;&#x2F;twig.sensiolabs.org&#x2F;) which are Handlebars for PHP...

What all these templating languages have in common is that they split your application logic across multiple contexts, typically without your awareness. The fancier the templating language, the more the temptation to build your application in a blindspot that you can&amp;#39;t easily debug. It&amp;#39;s like putting your diamonds in the one corner of the jewelry store where there aren&amp;#39;t any cameras.

## There must be a better way

There is. Write your application logic in javascript (or whatever your main application language happens to be). Stacktraces will tell you something useful, other people will help you on stackoverflow, and more importantly the your business logic will be expressed in a language that&amp;#39;s used by thousands of developers.

But don&amp;#39;t throw Handlebars away just yet -- we have to have some way to put data into HTML, and Handlebars (despite my grumblings here) can still be a good tool for that purpose. Here&amp;#39;s my ten commandments for safely using Handlebars:

### 1. Thou shalt print static values

Never compute **anything** in a template. Pour only finished data into the template. Lean on `{% raw %}{{var}}{% endraw %}` expressions as much as possible to display values.

### 2. Thou shalt not mutate variables

As data passes between different scopes, the same data should retain the same variable name and the same path. You should never do this:

```handlebars{% raw %}
{{firstName}}
{{#myHelper who=firstName}}
  {{who}}
{{&#x2F;myHelper}}
{% endraw %}```

When variables change names inside handlebars, it becomes exponentially harder to trace them from child scopes to parent scopes. This leads to misunderstandings of how the code works and bugs over time. When these bugs arise, they&amp;#39;re very costly to fix because of the effort of tracking them down.

### 3. Thou shalt (cautiously) use `#if` and `#each`

Collections of data are ubiquitous. Handlebars already has a built-in blocker helper for `{% raw %}{{#each}}{% endraw %}` that iterates over an array to produce html. Using `{% raw %}{{#each}}{% endraw %}` as an iterator is an excellent idea. It&amp;#39;s documented, has test coverage, and you can find support for it on stackoverflow&#x2F;irc&#x2F;etc.

Checking for the presence of a variable can be an acceptable way to structure a template. It&amp;#39;s okay to check for a user in the template context before showing  account information, for instance.

### 4. Thou shalt not `switch`

Checking for the presence of a variable is completely different than executing an extended else-if conditional&#x2F;pattern matching in templates. Your template is too dynamic -- move more logic back into the server.

### 5. Thou shalt not `else`

I&amp;#39;m gonna be stickler on this one. Your code _will_ be clearer without this extra logic. You&amp;#39;re building templates, not lambda calculus. Steer clear of any unnecessary conditional logic.

### 6. Thou shalt be LOOSE

The pythonistas have focused on the programming discipline called DRY. The key insight is that application logic works better with a single source of truth. Code that is duplicated often needs to be updated in sync, but can easily get out of sync.

Template logic is governed instead by the need to be loosely coupled. Overly dynamic templates are difficult to reason about because of the many codepaths they are tied to. It&amp;#39;s generally much easier to maintain multiple similar templates with isolated responsibilities than a single template with many responsibilities. When these similar template get of sync, they are easy to change because there are few side effects.

### 7. Thou shalt not use more than seven mustaches in a row

I just made up the number seven, but seriously though too many mustaches next to each other are unreadable. If you have that many mustaches consider moving more logic into client or server side javascript.

### 8. Thou shalt the use of minize partials

Partials are there to DRY up your code, which is a bad thing. Sometimes, you will absolutely have to reuse something, like the footer, across different templates. Consider ways to avoid creating a partial if possible, but don&amp;#39;t feel too bad doing it.

The real danger is that you&amp;#39;ll start passing very different contexts to the partial. By keeping as much of the template in a single file as possible, you avoid switching contexts and the possibility of mutating a variable.

### 9. Thou shalt program declaratively

One of the reasons that the sample code above is _so_ bad is the gratuitous use of procedural logic in the template. If you must use helpers, only use them declaratively, as shown in this Handlebars documentation snippet:

```handlebars{% raw %}
{{{link &amp;quot;See more...&amp;quot; href=story.url class=&amp;quot;story&amp;quot;}}}
{% endraw %}```

If there&amp;#39;s verb used in the handlebars helper **you did it wrong**.

### 10. Thou shalt create an API for client side rendering

If you want templates that can be rendered client side and server side, move all the data generation, templation selection, and other logic into the server and expose an api endpoint for your client side code to consume. This keeps your program logic DRY (as it should be) but keeps logic out of the templates (where it shouldn&amp;#39;t be).

## In Conclusion

Handlebars isn&amp;#39;t evil, but it can be a siren song luring you to dive overboard into a sea of unrelenting bugs. By keeping your programming logic in your main programming language (javascript in this example), you can keep your code easier to debug, easier to read, and easier to find help when you get stuck. When you do use Handlebars, keep it stupidly simple. You&amp;#39;ll thank me later.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</content>
	</entry>
	<entry xml:lang="en">
		<title>How to Kill Email - Why startups will always fail to displace email</title>
		<published>2014-11-21T00:00:00+00:00</published>
		<updated>2014-11-21T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/how-to-kill-email/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/how-to-kill-email/</id>
		<content type="html">&lt;p&gt;It&#x27;s painful to watch startup after startup brand themselves the &amp;quot;email killer.&amp;quot; This post is the real story of how to kill email.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;make-it-backwards-compatible&quot;&gt;Make It Backwards Compatible&lt;&#x2F;h2&gt;
&lt;p&gt;Email is so well entrenched in both business and personal communications that it cannot simply be &amp;quot;killed&amp;quot; outright. According to &lt;a href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Experian&quot;&gt;Experian&lt;&#x2F;a&gt; Marketing Services (a scary treasure trove of voting records, email marketing, credit history, rental history, etc.), the volume of &lt;a href=&quot;http:&#x2F;&#x2F;www.experian.com&#x2F;marketing-services&#x2F;email-marketing-quarterly-benchmark-study-q2-2013.html&quot;&gt;email traffic year over year continues to grow&lt;&#x2F;a&gt; by double digits. Radicati predicts continued &lt;a href=&quot;http:&#x2F;&#x2F;www.radicati.com&#x2F;wp&#x2F;wp-content&#x2F;uploads&#x2F;2014&#x2F;10&#x2F;Email-Market-2014-2018-Executive-Summary.pdf&quot;&gt;double digit growth in email volume&lt;&#x2F;a&gt; over the next four years. In the face of growing email volume, whatever replaces email must also be backwards compatible with it.&lt;&#x2F;p&gt;
&lt;p&gt;This is how git largely replaced svn -- developers could keep all their existing commit history when making the switch. Similarly, HTML5 was able to easily replace HTML4 because it mostly added tags and APIs on top of HTML4. Perhaps supporting POP3 (which was not built for the smartphone era) should be optional, but whatever kills email must support IMAP and SMTP.&lt;&#x2F;p&gt;
&lt;p class=&quot;responsive-image&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;flattop341&#x2F;212276638&quot;&gt;
&lt;img alt=&quot;Row of standardized XLR inputs from a recording studio&quot; src=&quot;&#x2F;img&#x2F;2014&#x2F;11&#x2F;xlr-standard-inputs.jpg&quot;&gt;
&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;create-a-new-email-standard&quot;&gt;Create a New Email Standard&lt;&#x2F;h2&gt;
&lt;p&gt;In a word, we need standards not apps to kill email. This is why so-called email killers such as Slack and Yammer are failing to dislodge email.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-do-you-mean-email-standard&quot;&gt;What Do You Mean, &amp;quot;Email Standard&amp;quot;?&lt;&#x2F;h3&gt;
&lt;p&gt;Roughly, I mean a protocol such as SMTP + IMAP. We need a clearly defined technical specification that has been hashed out by the Internet Enginerring TaskForce (IETF) or some other standards body with representatives from spam filtering companies, networking experts, systems administrators, government agencies, and email client vendors.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-consensus-is-so-important&quot;&gt;Why Consensus Is So Important&lt;&#x2F;h3&gt;
&lt;p&gt;Without consensus, broad adoption is impossible. HTML5, CSS3, and ES5 (to name a few) could not have come about without a consensus between browser makers and web developers. Today, it&#x27;s tough to find a website made in the last 3 years that doesn&#x27;t use some aspects of HTML5 or CSS3.&lt;&#x2F;p&gt;
&lt;p&gt;To take a parallel case from the online video technology industry (currently mine), MPEG-DASH has relatively strong consensus between video transcoding and CDN companies, but it seems that web developers were not given enough voice in the process. Ask any web developer about their experience with MPEG-DASH, and you&#x27;ll hear about the horrors of trying to parse XML in the browser, among other complaints. For this reason, MPEG-DASH hasn&#x27;t gotten much traction at this point in November 2014.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;other-reasons-why-apps-won-t-kill-email&quot;&gt;Other Reasons Why Apps Won&#x27;t Kill Email&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Battle-tested ideas not subject to whims of a few charismatic founders&lt;&#x2F;li&gt;
&lt;li&gt;Zero cost experimentation allowing a critical mass of technical innovation&lt;&#x2F;li&gt;
&lt;li&gt;Stability of APIs&lt;&#x2F;li&gt;
&lt;li&gt;Resilience to disappearing funding or acquisition&lt;&#x2F;li&gt;
&lt;li&gt;Competition from a variety of implementations&lt;&#x2F;li&gt;
&lt;li&gt;Protection against one company selling ALL user data&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of these could almost justify it&#x27;s own blog post -- if you&#x27;re curious about these, the comments at the bottom are the perfect place for a flame war. Game on!&lt;&#x2F;p&gt;
&lt;p class=&quot;responsive-image&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;pulpolux&#x2F;8773281&#x2F;in&#x2F;photolist-prR16B-CU2dB-f5sapZ-7yUnsk-dfZKru-99QG3B-94SntC-LXZt-4zSncV-4AxFbj-64prpn-dDUZiW-8wMxfK-7HHnxL-7FyKFj-8XY5Aa-ebV5vf-ebPqii-ebPA2T-aobYSE-inYagg-9cnwcz-ammwhv-ebPqfx-ebPqcB-ebVfC1-ebVfyf-ebPA6t-ebVfpo-e9bKmj-e965ut-e9bKpC-e965BH-e965xg-5LG7d-dbrJue-93yjzd-b8EDHc-5VtiJY-9UCYvF-brB7Ek-9TkQ12-dkYpsw-aQ43c6-9Xdpgh-9XawGP-9YUuaf-8KZuNr-7N9DAS-FRVqY&quot;&gt;
&lt;img src=&quot;&#x2F;img&#x2F;2014&#x2F;11&#x2F;irresistible-chocolate.jpg&quot; alt=&quot;Five chocolate eclairs seen closeup&quot;&gt;
&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;make-it-irrestible&quot;&gt;Make It Irrestible&lt;&#x2F;h2&gt;
&lt;p&gt;The benefits to this new email standard must be compelling and obvious. Without bells and whistles for all parties from sysadmins to email client vendors to end users, there&#x27;s no way to get traction.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;ve read this far, you know that my opinions alone are woefully inadequate for a standard. I imagine that whatever replaces email must at least provide the following features (nearly all of them relate to the problem of bad actors).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;feature-impostors-begone&quot;&gt;Feature: Impostors, Begone!&lt;&#x2F;h3&gt;
&lt;p&gt;Phishing attacks are devastating and nearly unpreventable using the current email protocols. In particular, high profile targets like CEOs are often the victims of carefully researched and planned phishing attacks. This has to stop. A Russian hacker must not be able to convince my Grandma that she is me.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;feature-want-to-spam-make-it-rain&quot;&gt;Feature: Want to Spam? Make it Rain&lt;&#x2F;h3&gt;
&lt;p&gt;Sending lots and lots of physical mail is expensive. Email should also be computationally expensive to send (perhaps using something analogous to mining for bitcoins?). I believe that shifting costs onto spammers would solve the spam epidemic we face today.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;caveat-never-miss-your-friends-updates&quot;&gt;Caveat: Never Miss Your Friends Updates&lt;&#x2F;h4&gt;
&lt;p&gt;The danger here is that if sending emails is too &amp;quot;expensive&amp;quot;, no one will use it. Spam mitigation must take into account the needs for legitimate notifications. For instance, Facebook really does need to send emails almost every time a user posts a comment. Perhaps some mechanism of trust (modeled on keybase.io or perhaps on the block chain) could be used to allow trusted, verified senders&#x27; messages to always be delivered. Admittedly, this is a hard problem, and it will require the experise of Facebook and serious cryptologists.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;feature-keep-your-viruses-to-yourself&quot;&gt;Feature: Keep Your Viruses to Yourself&lt;&#x2F;h3&gt;
&lt;p&gt;Malware transmitted via email is a huge problem. If we could &amp;quot;sanitize&amp;quot; email so that viruses could not be transmitted via attachments, I would force my brother-in-law to sign up in a heart beat.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;feature-make-the-conversation-stick-together&quot;&gt;Feature: Make The Conversation Stick Together&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s use a UUID to bind together messages on a thread. We could eliminate resending the entire thread, and make all clients present a time ordered sequence of emails grouped by the thread UUID.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;feature-tie-message-format-to-html5-css3&quot;&gt;Feature: Tie Message Format to HTML5&#x2F;CSS3&lt;&#x2F;h3&gt;
&lt;p&gt;The bad actor here is Microsoft Outlook (and every web based mail client like Gmail and Yahoo!). It&#x27;s still like 1995 for marketers and nonprofits sending pretty emails. It sucks and it has to end.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;concluding-thoughts&quot;&gt;Concluding Thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;Postal mail is more sophiscated than email these days. That&#x27;s a shame. It&#x27;s time for the tech community to stop talking about killing email and actually do it. However, we can&#x27;t do it alone. We need a shiny new standard with bells and whistels that&#x27;s broadly agreed upon, widely adopted, and backwards compatible. That&#x27;s how to kill email.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Securing SSH with Multiple Keys</title>
		<published>2014-09-04T00:00:00+00:00</published>
		<updated>2014-09-04T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/securing-ssh-with-multiple-keys/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/securing-ssh-with-multiple-keys/</id>
		<content type="html">&lt;p&gt;It&#x27;s a great idea to use specific ssh-key key pairs per service (or per repository even). Even if your keys are compromised, they don&#x27;t allow your attacker to access any other services. Unfortunately, these services often assume you&#x27;ll use the default key pair (id_rsa).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-get-organized&quot;&gt;1. Get Organized&lt;&#x2F;h2&gt;
&lt;p&gt;I like to make folders inside ~&#x2F;.ssh for each service (Github, AWS, BitBucket,
DigitalOcean, etc), and place various keypairs. EX:&lt;&#x2F;p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code&gt;
~&#x2F;.ssh&#x2F;
  | - id_rsa
  | - id_rsa.pub
  |
  | - github&#x2F;
  |   | - workuser
  |   | - workuser.pub
  |   | - other
  |   | - other.pub
  |
  | - bitbucket&#x2F;
      | - personaluser
      | - personaluser.pub
&lt;p&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;div&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To actually create the key pairs, use &lt;code&gt;ssh-keygen&lt;&#x2F;code&gt; in the terminal.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-upload-your-public-key&quot;&gt;2. Upload your public key&lt;&#x2F;h2&gt;
&lt;p&gt;All of these services have a settings page for adding SSH public keys. Add yours, and also include a descriptive label if you can. Usually, I describe which computer this public key came from so that I can revoke with confidence later on, if necessary.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-find-the-git-push-endpoint&quot;&gt;3. Find the Git Push endpoint&lt;&#x2F;h2&gt;
&lt;p&gt;For bitbucket and github, the endpoint is typically in this format:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;git@github.com:brycefisher&#x2F;defaulterrors.git&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s break that down:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;git&lt;&#x2F;em&gt; -- everything before &#x27;@&#x27; is the username for SSH&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;github.com&lt;&#x2F;em&gt; -- the actual hostname&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;brycefisher&#x2F;defaulterrors.git&lt;&#x2F;em&gt; -- path to the git repository&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;4-config-all-the-things&quot;&gt;4. Config all the things!&lt;&#x2F;h2&gt;
&lt;p&gt;Open up ~&#x2F;.ssh&#x2F;config in your favorite text editor. At the end add a new entry (separated by a new line above):&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Host&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; github
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Hostname&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; github.com
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;User&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; git
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;IdentityFile ~&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&#x2F;.ssh&#x2F;github&#x2F;brycefisher
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Choose whatever you want for &lt;code&gt;Host&lt;&#x2F;code&gt; -- that&#x27;s an alias you can use in ssh. Notice how all the pieces from 3. above mapped to this new entry in ~&#x2F;.ssh&#x2F;config. In particular, we used everything except the path to the git repo.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;5-add-a-git-remote&quot;&gt;5. Add a git remote&lt;&#x2F;h2&gt;
&lt;p&gt;Under the hood, git is using SSH, and it can access the alias we created earlier (github) to reach github. Our git remote will have to know the SSH alias and the path to the git repo.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; git remote add origin deferr:brycefisherfleig&#x2F;defaulterrors.git
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;6-make-sure-it-works&quot;&gt;6. Make Sure it Works&lt;&#x2F;h2&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; git push&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -u&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; origin master
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If the above command succeeds, you&#x27;ll see something like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Counting&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; objects: 3, done.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Writing&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; objects: 100% (3&#x2F;3)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; 262 bytes | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; bytes&#x2F;s, done.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Total&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; 3 (delta 0)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; reused 0 (delta 0)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;To&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; github:brycefisherfleig&#x2F;defaulterrors.git
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If instead you see:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Permission&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; denied (publickey)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fatal:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; Could not read from remote repository.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Please&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; make sure you have the correct access rights
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; the repository exists.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then re-check the steps above.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>7 Alternatives to Amazon CloudFront CDN</title>
		<published>2014-05-27T00:00:00+00:00</published>
		<updated>2014-05-27T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/7-alternatives-to-amazon-cloudfront-cdn/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/7-alternatives-to-amazon-cloudfront-cdn/</id>
		<content type="html">&lt;p&gt;AWS CloudFront has many bizarre quirks:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;only two invalidations of 1000 paths can run concurrently&lt;&#x2F;li&gt;
&lt;li&gt;each invalidation takes about 5-15 minutes to complete&lt;&#x2F;li&gt;
&lt;li&gt;there is no &amp;quot;clear all cache&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;configuring gzip compression is nontrivial&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;m comparing the alternatives to CloudFront for static web content (html, css, js, images), specifically &lt;a href=&quot;http:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;documentation&#x2F;services&#x2F;cdn&#x2F;&quot;&gt;Azure&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;cdnify.com&#x2F;learn&#x2F;api&quot;&gt;CDNify&lt;&#x2F;a&gt;, &lt;a href=&quot;http:&#x2F;&#x2F;cloudflare.com&quot;&gt;CloudFlare&lt;&#x2F;a&gt;, &lt;a href=&quot;http:&#x2F;&#x2F;cdnsun.com&#x2F;&quot;&gt;CDNSun&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;cdnify.com&#x2F;learn&#x2F;api&quot;&gt;Fastly&lt;&#x2F;a&gt;, &lt;a href=&quot;http:&#x2F;&#x2F;keycdn.com&quot;&gt;KeyCDN&lt;&#x2F;a&gt;, and &lt;a href=&quot;http:&#x2F;&#x2F;docs.maxcdn.com&#x2F;&quot;&gt;MaxCDN&lt;&#x2F;a&gt; . I&#x27;ve picked only CDNs that made pricing information available (which eliminated most of the options I could find). Pricing &lt;em&gt;is&lt;&#x2F;em&gt; a feature, and typically CDNs without pricing information didn&#x27;t disclose their feature set online either.&lt;&#x2F;p&gt;
&lt;p&gt;One CDN that looked fantastic but didn&#x27;t make the cut was &lt;a href=&quot;http:&#x2F;&#x2F;highwinds.com&#x2F;&quot;&gt;Highwinds&lt;&#x2F;a&gt;. They provide loads of documentation and a free trial, but no pricing information.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pricing-comparison&quot;&gt;Pricing Comparison&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;right&quot;&gt;CDN&lt;&#x2F;th&gt;&lt;th align=&quot;center&quot;&gt;Pricing Model&lt;&#x2F;th&gt;&lt;th align=&quot;right&quot;&gt;Costs (US)&lt;&#x2F;th&gt;&lt;th&gt;HTTPS Pricing&lt;&#x2F;th&gt;&lt;th align=&quot;left&quot;&gt;Free Option&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CloudFront&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Per GB&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;$0.12&lt;&#x2F;td&gt;&lt;td&gt;$0 - SNI&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y - 1 yr&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;Azure&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Per GB&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;$0.12&lt;&#x2F;td&gt;&lt;td&gt;Unknown&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CDNify&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Flat Rate + Over &lt;em&gt;150&lt;&#x2F;em&gt;GB&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;&lt;strong&gt;$10.00&lt;&#x2F;strong&gt; + $0.07&lt;&#x2F;td&gt;&lt;td&gt;$0&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;N&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CloudFlare&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Flat Rate&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;&lt;strong&gt;$20.00&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;$200&#x2F;mo Plan&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CDNSun&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Per GB&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;$0.45&lt;&#x2F;td&gt;&lt;td&gt;$699&#x2F;yr&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y - 15 days&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;Fastly&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Per Request + Per GB&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;&lt;em&gt;$0.01&lt;&#x2F;em&gt; + $0.12&lt;&#x2F;td&gt;&lt;td&gt;$500 setup + $100&#x2F;mon&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y - 30 days&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;KeyCDN&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Per GB&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;$0.04&lt;&#x2F;td&gt;&lt;td&gt;$0&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;N&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;MaxCDN&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Flat Rate + Over &lt;em&gt;100&lt;&#x2F;em&gt;GB&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;&lt;strong&gt;$9.00&lt;&#x2F;strong&gt; + $0.08&lt;&#x2F;td&gt;&lt;td&gt;$39&#x2F;mo&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;N&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Per GB&amp;quot; (normal font) refers to bandwidth charges&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;Flat Rate&amp;quot;&lt;&#x2F;strong&gt; (in bold) is per month fees. &amp;quot;Over ___ GB&amp;quot; specifies the included bandwidth&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;&amp;quot;Per Request&amp;quot;&lt;&#x2F;em&gt; (in italics) refers to individual requests between edge and client&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The standard going rate for CDN bandwith is 12¢. KeyCDN is the cheapest option, and CDNSun is priciest.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;network-comparison&quot;&gt;Network Comparison&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;right&quot;&gt;CDN&lt;&#x2F;th&gt;&lt;th align=&quot;center&quot;&gt;HTTPS&lt;&#x2F;th&gt;&lt;th align=&quot;right&quot;&gt;POPs&lt;&#x2F;th&gt;&lt;th align=&quot;right&quot;&gt;Latency&lt;&#x2F;th&gt;&lt;th align=&quot;left&quot;&gt;Gzip&lt;&#x2F;th&gt;&lt;th align=&quot;left&quot;&gt;Minification&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CloudFront&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Y&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;50&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;47&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y - in origin&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;N&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;Azure&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;?&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;30&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;47&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y - in origin&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;N&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CDNify&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Y&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;20&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;77&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;N&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CloudFlare&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Y&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;25&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;49&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CDNSun&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Y&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;85&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;?&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;?&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;Fastly&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Y&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;20&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;58&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;N&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;KeyCDN&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Y&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;15&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;?&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y - images only&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;MaxCDN&lt;&#x2F;td&gt;&lt;td align=&quot;center&quot;&gt;Y&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;15&lt;&#x2F;td&gt;&lt;td align=&quot;right&quot;&gt;44&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y - in origin&lt;&#x2F;td&gt;&lt;td align=&quot;left&quot;&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;ul&gt;
&lt;li&gt;There is no information about using HTTPS with Azure in the Microsoft documentation or pricing&lt;&#x2F;li&gt;
&lt;li&gt;&amp;quot;POPs&amp;quot; or Points of Presence refers to how many geographically separate data centers a CDN has (number of servers per data center can vary)&lt;&#x2F;li&gt;
&lt;li&gt;Latency is measured in milliseconds by &lt;a href=&quot;https:&#x2F;&#x2F;www.citrix.com&#x2F;products&#x2F;citrix-intelligent-traffic-management&#x2F;country-reports.html&quot;&gt;Citrix ITM&lt;&#x2F;a&gt; (formerly &amp;quot;Cedexis&amp;quot;) as of 5&#x2F;26&#x2F;2014&lt;&#x2F;li&gt;
&lt;li&gt;A &amp;quot;Y&amp;quot; for Gzip means that the CDN can transparently handle compression during transmission over the network for you. &amp;quot;Y - in origin&amp;quot; means your origin server must handle compression, but the CDN can intelligently serve compressed files&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The only big difference between these CDNs IMHO is whether or not the CDN can handle Gzip compression for you without configuring an origin server. Ideally, you could just dump your pre-minified files onto a cloud storage service such as AWS S3 or RackSpace CloudFiles and let the CDN handle this for.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;caching-behavior-comparison&quot;&gt;Caching Behavior Comparison&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;right&quot;&gt;CDN&lt;&#x2F;th&gt;&lt;th&gt;Headers&lt;&#x2F;th&gt;&lt;th&gt;Cookies&lt;&#x2F;th&gt;&lt;th&gt;Query String&lt;&#x2F;th&gt;&lt;th&gt;Protocol&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CloudFront&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;N&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;Azure&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CDNify&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CloudFlare&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;N&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CDNSun&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;Fastly&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;KeyCDN&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;MaxCDN&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Headers&amp;quot; refers to respecting the origin&#x27;s Cache-Control (or other caching specific) headers&lt;&#x2F;li&gt;
&lt;li&gt;All columns refer to the ability to vary the cached content served to visitors by examining this information&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The winner in this category appears to be Fastly, with CDNify providing virtually no information about how configurable its caching behavior is.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;configuration-comparison&quot;&gt;Configuration Comparison&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th align=&quot;right&quot;&gt;CDN&lt;&#x2F;th&gt;&lt;th&gt;Latency&lt;&#x2F;th&gt;&lt;th&gt;Purge All&lt;&#x2F;th&gt;&lt;th&gt;API&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CloudFront&lt;&#x2F;td&gt;&lt;td&gt;10 mins&lt;&#x2F;td&gt;&lt;td&gt;N&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;Azure&lt;&#x2F;td&gt;&lt;td&gt;mins?&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;td&gt;N&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CDNify&lt;&#x2F;td&gt;&lt;td&gt;mins?&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CloudFlare&lt;&#x2F;td&gt;&lt;td&gt;secs&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;CDNSun&lt;&#x2F;td&gt;&lt;td&gt;mins?&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;Fastly&lt;&#x2F;td&gt;&lt;td&gt;150 ms&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;KeyCDN&lt;&#x2F;td&gt;&lt;td&gt;5 mins&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;?&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;MaxCDN&lt;&#x2F;td&gt;&lt;td&gt;30 sec&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;td&gt;Y&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;latency&amp;quot; means the time required to update the edge servers with configuration changes (or purges&#x2F;invalidations)&lt;&#x2F;li&gt;
&lt;li&gt;Except for AWS CloudFront, all latency information is taken from the self-reporting of each company&#x27;s website&lt;&#x2F;li&gt;
&lt;li&gt;&amp;quot;mins?&amp;quot; means that the company did not state how long, and I assume changes will take minutes to propagate across edge servers&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Fastly comes out ahead in this category, with MaxCDN and CloudFlare close behind.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;and-the-winner-is&quot;&gt;And the Winner Is...&lt;&#x2F;h2&gt;
&lt;p&gt;For me, it&#x27;s a toss up between KeyCDN and MaxCDN. I really like the fast configuration updates that MaxCDN boasts, but I wish the SSL&#x2F;TLS pricing model was a bit cheaper. If you just want to get a CDN out there and don&#x27;t want to do much configuration, Key CDN seems like the way to go.&lt;&#x2F;p&gt;
&lt;p&gt;Fastly is my third place runner up. The incredible documentation, low latency, and extreme configurability make it the power house in the group. Sadly, hosting a custom SSL certificate on Fastly costs a small fortune.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Using AWS CloudFront Behaviors with HTTPS for Planet Drupal RSS</title>
		<published>2014-04-30T00:00:00+00:00</published>
		<updated>2014-04-30T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/using-aws-cloudfront-behaviors-with-https-for-planet-drupal-rss/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/using-aws-cloudfront-behaviors-with-https-for-planet-drupal-rss/</id>
		<content type="html">&lt;p&gt;After all the relevations of Snowden last year, I&#x27;ve been wanting to be more security focused. A small but obvious change is providing this blog over HTTPS. Disqus powers the comments, and if you login via Disqus I&#x27;d like to protect my visitors session cookies or authentication information. I felt so proud [to have setup a CDN powered, encrypted blog]({{ site.url }}{% post_url 2014-04-24-setting-up-ssl-on-aws-cloudfront-and-s3 %}).&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, I discovered that &lt;a href=&quot;http:&#x2F;&#x2F;feedvalidator.org&#x2F;check.cgi?url=https%3A%2F%2Fbryce.fisher-fleig.org%2Fcategories%2Fplanet-drupal%2Ffeed.xml&quot;&gt;RSS feed aggregators&lt;&#x2F;a&gt; and &lt;a href=&quot;http:&#x2F;&#x2F;validator.w3.org&#x2F;feed&#x2F;check.cgi?url=https%3A%2F%2Fbryce.fisher-fleig.org%2Fcategories%2Fplanet-drupal%2Ffeed.xml&quot;&gt;validators are incompatible with HTTPS&lt;&#x2F;a&gt;. Based on my experience, it seems that the Drupal.org aggregators that power the illustrious Planet Drupal also fail in the face of HTTPS.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;oh-behave-a-cloudfront-workaround-with-behaviors&quot;&gt;Oh, Behave - a CloudFront workaround with Behaviors&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;re using CloudFront, it&#x27;s actually very simple to serve your feeds over HTTP and redirect everything else to HTTPS. Let&#x27;s assume you already direct all your traffic to HTTPS. Here&#x27;s how to add an exception:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Open CloudFront in Web Console&lt;&#x2F;li&gt;
&lt;li&gt;Edit the Distribution&lt;&#x2F;li&gt;
&lt;li&gt;Click on &amp;quot;Behaviors&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;Add a new behavior, setting the following settings:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Path Pattern&lt;&#x2F;strong&gt; - setup a regex that matches your RSS feeds. I set all my RSS feeds as feed.xml, so for me this value was &amp;quot;*&#x2F;feed.xml&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Origin&lt;&#x2F;strong&gt; - choose your existing Origin from the dropdown list&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Viewer Protocol Policy&lt;&#x2F;strong&gt; - use the default setting here (this setting is the important one)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Save it!&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Once your distribution finishes updating the edge servers, you should be able to access your feed over HTTP (and HTTPS), but everything else should redirect to HTTPS. Tada!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-would-rss-aggregators-not-be-https-compatible&quot;&gt;Why Would RSS Aggregators Not Be  HTTPS Compatible?&lt;&#x2F;h2&gt;
&lt;p&gt;Dave Winer, one of the creators of feedvalidator.org, writes that switching the aggregator to HTTPS 
has a huge potential downside of losing parts of your audience with software that doesn&#x27;t support HTTPS. &lt;&#x2F;p&gt;
&lt;p&gt;Furthermore, he writes:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;... All aggregators will support HTTPS if enough developers of feeds require it.&lt;&#x2F;p&gt;
&lt;p&gt;If it should turn out that way, ... I&#x27;ll do it. And I&#x27;ll be pissed because it&#x27;s time I&#x27;d rather spend
doing something creative. ... I can&#x27;t see for the life of me why you need to push 
RSS over a secure connection. :-)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;dave-has-a-point&quot;&gt;Dave has a point&lt;&#x2F;h3&gt;
&lt;p&gt;It does seem unnecessary, actually, to encrypt connections between RSS feeds and aggregators. If the feed is meant
to be public, then hopefully no sensitive information is being transmitted in the feed. Also, I hate the drudgery
of meaningless work and would be loathe to impose it on anyone else.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;maybe-we-should-do-it-anyway&quot;&gt;Maybe we should do it anyway.&lt;&#x2F;h3&gt;
&lt;p&gt;Since December 2012, the new &lt;a href=&quot;https:&#x2F;&#x2F;www.owasp.org&#x2F;index.php&#x2F;HTTP_Strict_Transport_Security&quot;&gt;HSTS HTTP headers&lt;&#x2F;a&gt; have taken hold in browser-land. These headers allow servers to instruct browsers to only access the server over HTTPS for a certain period of time. It&#x27;s like a cache TTL.&lt;&#x2F;p&gt;
&lt;p&gt;HSTS headers are designed to prevent certain kinds of man-in-the-middle attacks. If a website is following the best practice of using HSTS headers in response to the pervasive threat that the NSA, GCHQ, China, Russia, and roving bands of hackers, then that site won&#x27;t be able to syndicate a feed from the HSTS domain.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, what about private RSS feeds? Shouldn&#x27;t we be able to leverage run of the mill aggregators for use cases such as for UPS package delivery? What about notification from the government that our new passport has been approved and will be shipped Monday? I can also imagine private feeds from Etrade notifying customers that an automated transaction occurred. The possibilities for private feeds are really endless, especially once one starts considering the coming Internet of Things.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Avoiding the Duplicate Content Penalty with AWS S3 and CloudFront</title>
		<published>2014-04-26T00:00:00+00:00</published>
		<updated>2014-04-26T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/avoiding-duplicate-content-penalty-with-aws-s3-and-cloudfront/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/avoiding-duplicate-content-penalty-with-aws-s3-and-cloudfront/</id>
		<content type="html">&lt;p&gt;As soon as I setup this blog on CloudFront using S3 as the origin server, my friends immediately started telling me I was at risk of &lt;a href=&quot;https:&#x2F;&#x2F;support.google.com&#x2F;webmasters&#x2F;answer&#x2F;66359?hl=en&quot;&gt;the duplicate content penalty&lt;&#x2F;a&gt; Google imposes on sites that have nearly identical content. After many hours of pouring over Stackoverflow to no avail, I found two strategies to protect my blog: canonical urls and user-agent based S3 bucket policies.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re hosting your blog or site on S3 without CloudFront, the duplicate content penalty is not an issue for you in the same way. Don&#x27;t attempt to use the S3 bucket policies described here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;strategy-1-user-agent-policies-in-aws-s3&quot;&gt;Strategy 1: User Agent Policies in AWS S3&lt;&#x2F;h2&gt;
&lt;p&gt;This strategy actively shields your content from being discoverable by search engine crawlers, so it&#x27;s likely to be much more effective. Unfortunately, it&#x27;s not fool proof and it&#x27;s dependent on AWS CloudFront implementation details that could change without any public notice. In particular, we&#x27;re relying on the User-Agent string that CloudFront uses for it&#x27;s GET requests to your S3 bucket. Here&#x27;s what to do:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Go your AWS Web Console and go to the S3 service&lt;&#x2F;li&gt;
&lt;li&gt;Expand the Permissions tab for your website&#x27;s bucket&lt;&#x2F;li&gt;
&lt;li&gt;Click &amp;quot;Edit Bucket Policy&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;The JSON snippet in the Policy Editor should look like this:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Version&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;:&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;2012-10-17&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Statement&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: [
        {
           &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Sid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;:&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;PublicReadGetObject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
           &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Effect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;:&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Allow&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
           &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Principal&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: {
                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;AWS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
            },
            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Action&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;:&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;s3:GetObject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Resource&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;:&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;arn:aws:s3:::bryce-fisher-fleig-org&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
        }
    ]
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Underneath the line starting with &amp;quot;Resource&amp;quot;, add this:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Condition&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: {
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;StringEqualsIgnoreCase&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: {
            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;aws:UserAgent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Amazon CloudFront&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
    }
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The whole should probably look something like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Version&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;2012-10-17&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Statement&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: [
        {
            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Sid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Allow CloudFront to read from Bucket&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Effect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Allow&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Principal&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: {
                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;AWS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
            },
            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Action&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;s3:GetObject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Resource&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;arn:aws:s3:::bryce-fisher-fleig-org&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Condition&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: {
                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;StringEqualsIgnoreCase&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: {
                    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;aws:UserAgent&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Amazon CloudFront&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
                }
            }
        }
    ]
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;did-it-work&quot;&gt;Did It Work??&lt;&#x2F;h3&gt;
&lt;p&gt;If you did everything right, your site should still be accessible through the CloudFront url. Check a url that hasn&#x27;t been cached in CloudFront yet (or do an invalidation). Go check right now, I&#x27;ll wait.&lt;&#x2F;p&gt;
&lt;p&gt;If new urls are coming through CloudFront okay, then make sure that your S3 website endpoint returns 403 Access Denied. Go, look up the website endpoint in your S3 console. If you received some kind of 403 error, Google&#x27;s crawler shouldn&#x27;t be able to access your S3 bucket directly. Congrats! No more duplicate content penalty for you.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;safety-first&quot;&gt;Safety First&lt;&#x2F;h3&gt;
&lt;p&gt;Obviously, you&#x27;ll need to change bryce-fisher-fleig-org to the name of your bucket. Also, you&#x27;ll need to make sure that whatever other permissions you have make sense with this rule, which is beyond the scope of this article.&lt;&#x2F;p&gt;
&lt;p&gt;I recommend setting up S3 to log into a separate bucket so that you can see the access log of what CloudFront is doing to your website bucket.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-cloudfront-origin-access-identity-sucks&quot;&gt;Why CloudFront Origin Access Identity Sucks&lt;&#x2F;h3&gt;
&lt;p&gt;As a quick aside, CloudFront does provide a mechanism that allows direct access between CloudFront and S3 without all this user agent smoke and mirrors nonsense. It&#x27;s called Origin Access Identity, but it&#x27;s incompatible with S3 when you enable the static website hosting mode for your bucket. If AWS gets around to fixing Origin Access Identity, you won&#x27;t need this hack anymore. Fingers crossed!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;strategy-2-canonical-url-links&quot;&gt;Strategy 2: Canonical Url Links&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s take a look at the second strategy to avoid duplicate content penalties: canonical urls in a link tag. All you have to do is a link tag with rel canoncial and href &lt;code&gt;http:&#x2F;&#x2F;your&#x2F;real&#x2F;url&#x2F;here&lt;&#x2F;code&gt;. You can look at the source code of this page, or do something like the following:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;link &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;rel&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;canonical&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;bryce.fisher-fleig.org&#x2F;blog&#x2F;avoiding-duplicate-content-penalty-with-aws-s3-and-cloudfront&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This technique is much weaker in that we&#x27;re providing suggestions to Google&#x27;s crawlers, but we can&#x27;t force the crawlers to comply. Nevertheless, if your mother always taught you it&#x27;s not polite to user agent sniff, canonical url links are completely kosher. Also, they won&#x27;t randomly take your site down when CloudFront gets a new user agent.&lt;&#x2F;p&gt;
&lt;p&gt;This technique can be really handy on Drupal sites (which provide most content at two urls) or &lt;a href=&quot;https:&#x2F;&#x2F;yoast.com&#x2F;canonical-url-links&#x2F;&quot;&gt;potentially for marketing campaigns with various url parameters or in other situations&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Setting Up SSL on AWS CloudFront and S3</title>
		<published>2014-04-24T00:00:00+00:00</published>
		<updated>2014-04-24T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/setting-up-ssl-on-aws-cloudfront-and-s3/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/setting-up-ssl-on-aws-cloudfront-and-s3/</id>
		<content type="html">&lt;p&gt;I setup this blog using S3 as the origin server, CloudFront as my CDN, and HTTPS for roughly $10. Since there were so many articles to read along the way, I&#x27;ve gathered up what worked for me here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-0-background-assumptions&quot;&gt;Step 0: Background &amp;amp; Assumptions&lt;&#x2F;h2&gt;
&lt;p&gt;CloudFront offers HTTPS at no additional charge in certain situations. CloudFront takes advantage of an extension to the TLS protocol called Server Name Indication (&amp;quot;SNI&amp;quot;), which allows servers on a single IPv4 address to serve multiple domains over HTTPS. It&#x27;s a huge cost savings for Amazon, and they&#x27;ve decided to pass on the savings to you. Pay attention to step 5 below to take advantage of SNI. You&#x27;ll still have to buy your own SSL certificate.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll assume you&#x27;re already using Jekyll or another static site generator to create HTML files that you plan to host on S3. Throughout this tutorial I&#x27;ll use the domain name of this blog to help you understand. You&#x27;ll want to replace &amp;quot;bryce.fisher-fleig.org&amp;quot; with your domain name anywhere you see it below.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-1-create-an-s3-bucket&quot;&gt;Step 1: Create an S3 Bucket&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Bucket Name:&lt;&#x2F;strong&gt; Your bucket name must be like &lt;em&gt;bryce-fisher-fleig-org&lt;&#x2F;em&gt; to work with CloudFront (bryce.fisher-fleig.org won&#x27;t work).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Permissions:&lt;&#x2F;strong&gt; Grant everyone read access to this bucket so that CloudFront can read out the content. Here&#x27;s how:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Open the Change the Permissions tab on your shiney new bucket&lt;&#x2F;li&gt;
&lt;li&gt;Click &amp;quot;Edit bucket policy&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;Copy and paste this JSON snippet into the policy editor:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Version&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;:&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;2012-10-17&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Statement&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;:[{
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Sid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;:&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;PublicReadGetObject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Effect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;:&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Allow&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Principal&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: {
      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;AWS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
    },
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Action&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;:[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;s3:GetObject&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;],
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Resource&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;:[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;arn:aws:s3:::bryce-fisher-fleig-org&#x2F;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;]
  }]
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Change &amp;quot;bryce-fisher-fleig-org&amp;quot; above to your bucket name&lt;&#x2F;li&gt;
&lt;li&gt;Click Save&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;strong&gt;Static Website Hosting&lt;&#x2F;strong&gt; Open this tab and select &amp;quot;Enable website hosting&amp;quot;. Make sure to set your &lt;em&gt;Index document&lt;&#x2F;em&gt; to index.html so that folders work right. Keep the &amp;quot;Endpoint&amp;quot; url handy for later.&lt;&#x2F;p&gt;
&lt;p&gt;This would be a great time to start uploading all the things to your new bucket, and try clicking on the Endpoint url to make sure things are groovy.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-2-create-a-cloudfront-distribution&quot;&gt;Step 2: Create a CloudFront Distribution&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;Choose &amp;quot;Web&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Origin Domain Name&lt;&#x2F;strong&gt; - Paste the &amp;quot;Endpoint&amp;quot; from S3&#x27;s Static Website Hosting tab here&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Minimum TTL&lt;&#x2F;strong&gt; - Click &amp;quot;Customize&amp;quot;. Keep this at 5 minutes (360 seconds). Once everything is groovy, change it to something higher (for instance, 24 hours).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Price Class&lt;&#x2F;strong&gt; - I use US, Europe, and Asia. Choose whatever you&#x27;re comfortable paying for.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Alternate Domain Names&lt;&#x2F;strong&gt; - Enter &amp;quot;bryce.fisher-fleig.org&amp;quot; (or whatever your subdomain is). You&#x27;ll have to &lt;a href=&quot;http:&#x2F;&#x2F;docs.aws.amazon.com&#x2F;Route53&#x2F;latest&#x2F;DeveloperGuide&#x2F;CreatingNewDNS.html&quot;&gt;use Route53 if you want to use a root domain&lt;&#x2F;a&gt; with CloudFront.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Default Root Object&lt;&#x2F;strong&gt; enter &amp;quot;index.html&amp;quot;. Otherwise, http:&#x2F;&#x2F;bryce.fisher-fleig.org&#x2F; wouldn&#x27;t display the root index.html file.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Don&#x27;t worry about the various HTTPS settings yet, we&#x27;ll come back to that later. None of the other settings need to be changed either.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-3-obtain-an-ssl-certificate&quot;&gt;Step 3: Obtain an SSL Certificate&lt;&#x2F;h2&gt;
&lt;p&gt;This step walks you through paying a company called a Certificate Authority (&amp;quot;CA&amp;quot;) to verify your identity and issue an SSL certificate with their name on it. No matter which CA you pick, you&#x27;ll have to create a Certificate Signing Request (CSR) which you will upload to the CA. You&#x27;ll also need to generate a matching file called a private key.&lt;&#x2F;p&gt;
&lt;p&gt;The CA&#x27;s act as a kind of notary public for the internet. Anyone can generate an SSL certificate for free, but if your certificate isn&#x27;t signed by an official CA, then Chrome, Firefox, Safari, etc will not allow visitors to access your site over HTTPS. There&#x27;s a limited number of these Certificate Authorities around, thus they can and do charge obscene prices. Resellers, like the two I mention below, offer much better prices.&lt;&#x2F;p&gt;
&lt;p&gt;Different CA&#x27;s offer different kinds of certificates with various degrees of verification and encryption. For a personal blog, choose a single domain certificate verified using Domain Validation (&amp;quot;DV&amp;quot;). If you&#x27;re doing e-commerce, you probably want the more complicated and expensive process called Extended Validation (&amp;quot;EV&amp;quot;).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-3-a-buy-an-ssl-certificate-on-the-cheap&quot;&gt;Step 3.A: Buy an SSL Certificate on the Cheap&lt;&#x2F;h3&gt;
&lt;p&gt;The best place I can find legit certs is &lt;a href=&quot;https:&#x2F;&#x2F;www.clickssl.com&#x2F;?post_type=product&quot;&gt;ClickSSL.com&lt;&#x2F;a&gt;. They provide an awesome search filter, and their certificates are all signed using SHA-2 (more on this below). All certificates have 2048-bit key length meaning that your certificate has reasonable encryption strength for the near future.&lt;&#x2F;p&gt;
&lt;p&gt;Previously, I advocated using CheapestSSLs.com, but I&#x27;ve discovered that they can have slow customer service and weaker encryption. It&#x27;s unclear if they support SHA-2. CheapestSSLs.com is a little cheaper than ClickSSL.com.&lt;&#x2F;p&gt;
&lt;p&gt;With either reseller, my main criteria for a certificate are:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;SHA-2&lt;&#x2F;strong&gt; (sometimes also called &amp;quot;SHA-256&amp;quot;) -- Chrome and Firefox have pledged to start showing really scary security warnings to users for sites using the older SHA-1 signing algorithm by 2017. They already print warnings in the console today.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Domain Validation&lt;&#x2F;strong&gt; (aka &amp;quot;DV&amp;quot;) -- this means you can get the certificate right away through a self-service process. This option is only available for single domain certificates.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Price&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;At my renewal time, I chose the RapidSSL certificate for this domain because I only want to serve one domain using this certificate, and the price was only $27 for two years. I highly recommend buying a two year certificate so that you don&#x27;t have to go through this process every single year.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-3-b-generate-a-matching-private-key-and-csr&quot;&gt;Step 3.B: Generate a matching private key and CSR&lt;&#x2F;h3&gt;
&lt;p&gt;Once you&#x27;ve paid for a certificate, generate a private key and a certificate signing request (CSR) using this command in the terminal:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; openssl req \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;    -sha256 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;    -new -newkey &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;\
    rsa:2048&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -nodes &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;    -keyout&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; bryce_fisher-fleig_org.key \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;    -out&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; bryce_fisher-fleig_org.csr
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;em&gt;(There&#x27;s a great article on what this command is doing at &lt;a href=&quot;http:&#x2F;&#x2F;www.entrust.net&#x2F;knowledge-base&#x2F;technote.cfm?tn=8231&quot;&gt;entrust.net&lt;&#x2F;a&gt;, but don&#x27;t worry too much about it.)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Openssl will ask you a series of questions once you enter the command above:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Country Name&lt;&#x2F;strong&gt; - two letter &lt;a href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ISO_country_codes#Officially_assigned_code_elements&quot;&gt;ISO 3166-1 code for your country&lt;&#x2F;a&gt; (e.g. &amp;quot;US&amp;quot;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;State or Province&lt;&#x2F;strong&gt; - e.g. &amp;quot;California&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Locality Name&lt;&#x2F;strong&gt; - e.g. &amp;quot;San Francisco&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Organization Name&lt;&#x2F;strong&gt; - your DBA, LLC, corp, or personal name. E.g. &amp;quot;Bryce Fisher-Fleig&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Organizational Unit&lt;&#x2F;strong&gt; - optional, type &amp;quot;.&amp;quot; to leave blank&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Common Name&lt;&#x2F;strong&gt; - This is the one you MUST get right. Use the domain name you want to serve over HTTPS. E.g. &amp;quot;bryce.fisher-fleig.org&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Email address&lt;&#x2F;strong&gt; - This is the email that will be exposed to spammers on the public certificate. You must have it, but consider creating a dedicated email address here for spam to collect in. E.g. &amp;quot;bff.dns.spam@gmail.com&amp;quot;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Later on, we&#x27;ll provide this private key to CloudFront, but it&#x27;s vital that you keep the private key very, very private. The security of your website depends on the private key being accessible only to you and CloudFront. You should never email or share this file with anyone for any reason, and you shouldn&#x27;t store this file Dropbox or Google Drive. If the key is ever compromised, many CA&#x27;s will allow you regenerate your certificate for free.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-3-c-upload-the-csr-to-your-certificate-authority&quot;&gt;Step 3.C: Upload the CSR to your Certificate Authority&lt;&#x2F;h3&gt;
&lt;p&gt;This part was very simple for me with both Comodo and RapidSSL. I did have to re-enter some details that I entered on the CSR such as the domain name.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-3-d-get-validated&quot;&gt;Step 3.D: Get validated!&lt;&#x2F;h3&gt;
&lt;p&gt;Honestly, verifying that I owned the domain is the worst part every time I&#x27;ve done this. I can&#x27;t complain about price or customer service, but the process seemed to take forever. Thank yourself for choosing Domain Validation now, because you&#x27;ve saved yourself a week of effort.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s three ways to complete the DV process:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Click a link sent to a special &lt;strong&gt;email address&lt;&#x2F;strong&gt; (usually administrator@fisher-fleig.org). This seems the hardest to me, because you have to setup the email server ahead of time, add MX records, create email accounts, and possibly setup anti-spam measures. I try never to do this, but this is only option for RapidSSL. If you have to go this route, I highly recommend using &lt;a href=&quot;https:&#x2F;&#x2F;www.gandi.net&#x2F;&quot;&gt;gandi.net&lt;&#x2F;a&gt;. They provide all the email setup for you as part of all domain name purchases, and their prices are very reasonable.&lt;&#x2F;li&gt;
&lt;li&gt;Host a &lt;strong&gt;file&lt;&#x2F;strong&gt; at a special url on that domain. This is tricky, but usually easier than email. Comodo supports this option. If you use S3, just upload the file they give you to S3.&lt;&#x2F;li&gt;
&lt;li&gt;Set a dns &lt;strong&gt;TXT Record&lt;&#x2F;strong&gt;. This is easiest possible way. Unfortunately, I&#x27;ve never used a CA that supported this option. If you find one, please tell me in the comments!&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If you don&#x27;t hear anything right away, you failed the domain validation. Don&#x27;t wait! Contact customer support immediately to help you resolve the issue. I&#x27;ve found support helpful, but the response time was unbearably slow (24hrs or more).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-4-upload-the-certificate-to-iam&quot;&gt;Step 4: Upload the Certificate to IAM&lt;&#x2F;h2&gt;
&lt;p&gt;Amazon stores all of the SSL certificates used by any of the AWS services inside it&#x27;s Identity Access and Management (IAM) service. In order to upload an SSL certificate you &lt;strong&gt;must create an IAM user with sufficient permissions&lt;&#x2F;strong&gt;. The root account user will not work.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;create-an-admin-iam-user&quot;&gt;Create an admin IAM user:&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;On the AWS Web Console, go to the IAM service&lt;&#x2F;li&gt;
&lt;li&gt;Click on the &amp;quot;Users&amp;quot; tab&lt;&#x2F;li&gt;
&lt;li&gt;Click &amp;quot;Create New Users&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;Enter the first user name. E.g. &amp;quot;SslKingPin&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;Click &amp;quot;Create&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;Show or download the credentials -- we&#x27;ll need them a little bit later.&lt;&#x2F;li&gt;
&lt;li&gt;Click on the newly created user&lt;&#x2F;li&gt;
&lt;li&gt;In the user console at the bottom of the page, choose the &amp;quot;Permissions&amp;quot; tab&lt;&#x2F;li&gt;
&lt;li&gt;Click &amp;quot;Attach User Policy&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;Copy and paste this JSON snippet into the Policy Editor and click save:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;{
  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Version&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;2012-10-17&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Statement&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: [{
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Effect&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Allow&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;,
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Action&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;],
    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Resource&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;: [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;]
  }]
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The JSON snippet above gives this user all privileges, so be careful!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setup-aws-cli&quot;&gt;Setup AWS Cli&lt;&#x2F;h3&gt;
&lt;p&gt;For Mac just use homebrew:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; brew up
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; brew install aws-cfn-tools awscli
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On Debian&#x2F;Ubuntu, just apt-get:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo apt-get install python python-pip
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo pip install awscli
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For other platforms, see the &lt;a href=&quot;http:&#x2F;&#x2F;docs.aws.amazon.com&#x2F;cli&#x2F;latest&#x2F;userguide&#x2F;cli-chap-getting-set-up.html&quot;&gt;official AWS documentation&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;configure-the-aws-cli&quot;&gt;Configure the AWS Cli:&lt;&#x2F;h3&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; aws configure
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The only tricky part here is to make sure you enter the IAM credentials for our SslKingPin user. The other settings are a matter of preference.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;finally-upload-the-damn-certificate&quot;&gt;Finally! Upload the Damn Certificate:&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;Download the zip file from Comodo containing your new SSL certificate.&lt;&#x2F;li&gt;
&lt;li&gt;Unzip all the files into the same directory you have the CSR and private key files.&lt;&#x2F;li&gt;
&lt;li&gt;Follow these steps to upload your ssl certificate to IAM:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; cd &#x2F;path&#x2F;to&#x2F;certificate&#x2F;files&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; aws iam upload-server-certificate \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --server-certificate-name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; bryce_fisher-fleig_org \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --certificate-body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; file:&#x2F;&#x2F;bryce_fisher-fleig_org.crt \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --private-key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; file:&#x2F;&#x2F;bryce_fisher-fleig_org.key \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --certificate-chain&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; file:&#x2F;&#x2F;PostivieSSLCA2.crt \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; &#x2F;cloudfront&#x2F;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This command is REALLY tricky to get right. So, lets break it down a bit:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;server-certificate-name&lt;&#x2F;strong&gt; This is just an alpha-numeric label you see inside AWS consoles. It doesn&#x27;t matter to anyone except you.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;certificate-body&lt;&#x2F;strong&gt; The certificate from Comodo&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;private-key&lt;&#x2F;strong&gt; This is the very first crypto file we made in Step 3.B.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;certificate-chain&lt;&#x2F;strong&gt; Using Comodo, this turned out to be the PositiveSSLCA2.crt file. It&#x27;s basically any files between but not including the trusted root certificate and the certificate with your domain name on it.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;path&lt;&#x2F;strong&gt; This ends in a slash, and it must start with &#x2F;cloudfront&#x2F;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;step-5-configure-cloudfront-to-use-ssl&quot;&gt;Step 5: Configure CloudFront to use SSL&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;Inside the CloudFront dashboard, edit the distribution you created in step 2.&lt;&#x2F;li&gt;
&lt;li&gt;On the General tab, click &amp;quot;edit&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;SSL Certificate&lt;&#x2F;strong&gt; - choose &amp;quot;Custom SSL Certificate (stored in AWS IAM)&amp;quot; and choose the certificate name from step 4 in the drop down (e.g. &amp;quot;BryceFisherFleigOrg&amp;quot;).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Custom SSL Client Support&lt;&#x2F;strong&gt; - choose &amp;quot;Only Clients that Support Server Name Indication (SNI)&amp;quot;. This saves you tons and tons of money, but means older versions of IE and Android can&#x27;t use the SSL.&lt;&#x2F;li&gt;
&lt;li&gt;Click &amp;quot;Yes, Edit&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;Click the &amp;quot;Behaviors&amp;quot; tab&lt;&#x2F;li&gt;
&lt;li&gt;Edit the behavior there.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Viewer Protocol Policy&lt;&#x2F;strong&gt; - choose &amp;quot;redirect HTTP to HTTPS&amp;quot; to force everyone to use SSL when viewing your site.&lt;&#x2F;li&gt;
&lt;li&gt;Click &amp;quot;Yes, Edit&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;Go &amp;quot;back to distributions&amp;quot;. You&#x27;ll see that the status is &amp;quot;In Progress&amp;quot; until the change takes effect all over the world.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;step-6-point-your-dns-to-cloudfront&quot;&gt;Step 6: Point your DNS to CloudFront&lt;&#x2F;h2&gt;
&lt;p&gt;From the CloudFront distribution screen, copy the &amp;quot;Domain Name&amp;quot; value. It will be something like zxy9qwnududu7.cloudfront.net. Go to your DNS nameservers and create a CNAME for this value. These changes will take some time to propagate fully (for me about 10-15 minutes, but your mileage may vary).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Did you find an error in my tutorial? Did something not make sense? Tweet or comment below and I&#x27;d love to help you figure this out. Happy blogging!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>A Super Handy Translation Checklist</title>
		<published>2014-03-14T00:00:00+00:00</published>
		<updated>2014-03-14T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/super-handy-translation-checklist/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/super-handy-translation-checklist/</id>
		<content type="html">&lt;p&gt;I&#x27;ve been bitten time after time during (what I wrongly assumed) would be the last phase of a project because of translation issues. So, I&#x27;ve put together a checklist of some DO&#x27;s and DON&#x27;Ts that I&#x27;ve learned that help me review someone else&#x27;s code or even my own code before forking over the big bucks to a translation company.&lt;&#x2F;p&gt;
&lt;p&gt;This checklist will be most helpful if you use Drupal as your CMS, and you send your translation vendor XLIFF and POT files. Also, this checklist assumes you have i18n already setup on your Drupal instance, and you&#x27;re looking to deploy a new feature or set of content. Enjoy!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;drupal-php&quot;&gt;Drupal&#x2F;PHP&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt; Proper use of t()
 &lt;ol&gt;
 &lt;li&gt;All text wrapped in t()&lt;&#x2F;li&gt;
 &lt;li&gt;All t() strings contain a coherent thought for translators to work with
  &lt;ul&gt;
  &lt;li&gt;rule of thumb, make sure each t() is fed at least 3 words&lt;&#x2F;li&gt;
  &lt;li&gt;No t() invocations wrapped within another t()&lt;&#x2F;li&gt;
  &lt;li&gt;Include FontAwesome or other image font tags into t() where it is a semantic part of the text content. (For instance, in Korean, it might be correct to place the glyph in the middle of a string, or at the beginning, whereas in English it must come last)&lt;&#x2F;li&gt;
  &lt;&#x2F;ul&gt;
 &lt;&#x2F;li&gt;
 &lt;li&gt;Avoid line breaks in t()
  &lt;ul&gt;
  &lt;li&gt;All text must flow naturally. Line breaking is something you must let go of (embrace the Zen)&lt;&#x2F;li&gt;
  &lt;&#x2F;ul&gt;
 &lt;&#x2F;li&gt;
 &lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;All links to localized content will localize (ex: lookup the nid of node translations and use url() or l() )&lt;&#x2F;li&gt;
&lt;li&gt;For multibyte text that needs to be used by JS, render text inside tags with explicit IDs&lt;&#x2F;li&gt;
&lt;li&gt;All public facing pages are attached to nodes (ajax urls are an exception)&lt;&#x2F;li&gt;
&lt;li&gt;No public facing pages use hook_menu&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;js&quot;&gt;JS&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;Avoid language specific paths in Ajax requests wherever possible&lt;&#x2F;li&gt;
&lt;li&gt;Avoid passing strings around in JS when working with multibyte strings (Asian characters)&lt;&#x2F;li&gt;
&lt;li&gt;Do use JS polyfills to support CSS powered buttons, etc in old browsers (progressive enhancement)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;css&quot;&gt;CSS&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;All elements containing visible text can expand 2X vertically (or horizontally) without breaking the layout&lt;&#x2F;li&gt;
&lt;li&gt;Background images on all expandable boxes gracefully stretch or contract (good use of &lt;code&gt;background-size&lt;&#x2F;code&gt; or gradients)&lt;&#x2F;li&gt;
&lt;li&gt;No side-by-side floated divs suddenly wrap to next line (unexpectedly) when content grows or shrinks&lt;&#x2F;li&gt;
&lt;li&gt;Avoid narrow columns (less than 300px) as much as possible&lt;&#x2F;li&gt;
&lt;li&gt;No textual string content inside pseudo-element &lt;code&gt;content&lt;&#x2F;code&gt; rules -- use t() instead&lt;&#x2F;li&gt;
&lt;li&gt;Use HTC polyfills (css3pie, etc) if your project requires you to support old versions of IE&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;imgs&quot;&gt;IMGs&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;Check that NO images referenced in HTML have text&lt;&#x2F;li&gt;
&lt;li&gt;Checked that NO background images referenced in CSS contain text&lt;&#x2F;li&gt;
&lt;li&gt;Avoid image based buttons (use CSS instead wherever possible)&lt;&#x2F;li&gt;
&lt;li&gt;Avoid image based gradients (use CSS wherever possible)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;architecture&quot;&gt;Architecture&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;All public facing pages are use nodes to exploit url aliases (except for ajax urls)&lt;&#x2F;li&gt;
&lt;li&gt;No public facing pages use hook_menu&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;communication&quot;&gt;Communication&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;Prepared a list of nodes that need aliases and (re)translation for translation team&lt;&#x2F;li&gt;
&lt;li&gt;Prepared a list of tokens (ex: !token, @token, %token) that must NOT be translated to give to translation team&lt;&#x2F;li&gt;
&lt;li&gt;Prepared a list of other quirks that the translation team needs to know about&lt;&#x2F;li&gt;
&lt;li&gt;Prepared some &amp;quot;next steps&amp;quot; for the translation team once they&#x27;ve finished &lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>PHP: What Does function ...() use () syntax mean?</title>
		<published>2014-01-31T00:00:00+00:00</published>
		<updated>2014-01-31T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/php-what-does-function-use-syntax-mean/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/php-what-does-function-use-syntax-mean/</id>
		<content type="html">&lt;p&gt;I&#x27;ve recently seen an unfamiliar syntax all over the place, but I didn&#x27;t understand it and I couldn&#x27;t find anything about it while searching. Here&#x27;s a sample:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;loop&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;onReadable&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;server&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;server&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;loop&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;});
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;igorw&#x2F;webserver-zceu&#x2F;blob&#x2F;master&#x2F;08-async-echo.php&quot;&gt;From igorw&#x27;s github&lt;&#x2F;a&gt;. Did you catch it? Why is there a &amp;quot;use&amp;quot; statement after a function? Googling &amp;quot;php use function&amp;quot; wasn&#x27;t any help. &lt;&#x2F;p&gt;
&lt;h2 id=&quot;buried-deep-in-the-php-manual-a-clue&quot;&gt;Buried Deep in the PHP Manual - A Clue&lt;&#x2F;h2&gt;
&lt;p&gt;After an hour of wandering through the PHP documentation (which I actually quite like doing anyway), I discovered this &lt;a href=&quot;http:&#x2F;&#x2F;www.php.net&#x2F;manual&#x2F;en&#x2F;language.namespaces.php#104136&quot;&gt;very helpful comment&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The keyword &#x27;use&#x27; has two different applications, but the reserved word table links to here.&lt;&#x2F;p&gt;
&lt;p&gt;It can apply to namespace constucts:&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Namespaces are pretty cool. They form the foundation of PSR-0, Composer, and everything good in the PHP these days. But, that syntax is used at the top of files on it&#x27;s own line. Then, I found the answer:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &#x27;use&#x27; keyword also applies to closure constructs:&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;closures-a-javascripty-world-within&quot;&gt;Closures: A (JavaScripty) World Within&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve seen the term &amp;quot;closure&amp;quot; in PHP before, but I&#x27;m most familiar with it inside Javascript. A closure in PHP simply refers to &lt;em&gt;any&lt;&#x2F;em&gt; anonymous function, even in the global scope. However, PHP 5.3 introduced this sneaky new syntax whereby you can copy variables out of the parent scope and into the &amp;quot;closure&amp;quot; scope. Nifty, huh? Here&#x27;s what &lt;a href=&quot;http:&#x2F;&#x2F;www.php.net&#x2F;manual&#x2F;en&#x2F;functions.anonymous.php&quot;&gt;the PHP documentation has to say on the use syntax&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Closures may also inherit variables from the parent scope. Any such variables must be passed to the &lt;code&gt;use&lt;&#x2F;code&gt; language construct. Inheriting
variables from the parent scope is not the same as using global variables. Global variables exist in the global scope, which is the same
no matter what function is executing. The parent scope of a closure is the function in which the closure was declared (not necessarily the
function it was called from).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;parameters-by-any-other-name&quot;&gt;Parameters by Any Other Name&lt;&#x2F;h2&gt;
&lt;p&gt;Just like normal function parameters, parameters provided to the closure scope via the &lt;code&gt;use&lt;&#x2F;code&gt; keyword are &lt;strong&gt;passed by value&lt;&#x2F;strong&gt;. To pass parameters by reference, simply add an ampersand (&amp;amp;) in front of the parameter.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;exciting-uses-of-closures-and-the-use-keyword&quot;&gt;Exciting Uses of Closures and the Use Keyword&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve been trying to figure out to hack together a pure PHP server using PHP 5.3 for a special side project, and I came upon the excellent static site generator called &lt;a href=&quot;http:&#x2F;&#x2F;github.com&#x2F;sculpin&#x2F;sculpin&quot;&gt;Sculpin&lt;&#x2F;a&gt;. It&#x27;s PHAR can be called from the command line just like Jekyll. It also has a special flag &lt;code&gt;--server&lt;&#x2F;code&gt; that spins up a server on the spot, even using PHP 5.3! Intrigued, I dug into the source code and finally found an &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sculpin&#x2F;sculpin&#x2F;blob&#x2F;master&#x2F;src&#x2F;Sculpin&#x2F;Bundle&#x2F;SculpinBundle&#x2F;HttpServer&#x2F;HttpServer.php&quot;&gt;evented PHP server inspired by NodeJS&lt;&#x2F;a&gt;. Here&#x27;s a few key parts of the code:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;httpServer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ReactHttpServer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;socketServer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);
$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;httpServer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;on&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;repository&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;docroot&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;output&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
    $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;path &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;docroot&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;ltrim&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;rawurldecode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;getPath&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;()), &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;);
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;is_dir&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)) {
        $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;path &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.= &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;index.html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;;
    }
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(!&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;file_exists&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)) {
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;HttpServer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;logRequest&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;output&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;404&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);
        $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;writeHead&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;404&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;end&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();
    }

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ...

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;writeHead&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Content-Type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot; =&amp;gt; $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,
    ));
    $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;end&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;file_get_contents&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;));
});
$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;socketServer&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;listen&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;port&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;0.0.0.0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;a-brighter-future&quot;&gt;A Brighter Future&lt;&#x2F;h2&gt;
&lt;p&gt;I really hope that the future of PHP is a future that&#x27;s less tied to Apache. That worked well in the past, but it&#x27;s just too hard to provision and tweak Apache when you just want to write. Source code like this inspires me to believe that it&#x27;s possible to handle all of our routing logic without having to rely on some other piece of software to ferry requests from the network to our application and back again.&lt;&#x2F;p&gt;
&lt;p&gt;Where have you seen &lt;code&gt;use&lt;&#x2F;code&gt; done right? Let me know your thoughts on this and other esoterica from recent versions of PHP.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Using hook_menu to capture custom menus in code</title>
		<published>2014-01-14T00:00:00+00:00</published>
		<updated>2014-01-14T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/using-hook-menu-to-capture-custom-menus-in-code/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/using-hook-menu-to-capture-custom-menus-in-code/</id>
		<content type="html">&lt;p&gt;Every time I use &lt;code&gt;hook_menu()&lt;&#x2F;code&gt;, I get an awful headache. Why? Drupal has overloaded this hook to handle both the menu system and the routing system, which seems like an architectural mistake to me. As a result, &lt;code&gt;hook_menu()&lt;&#x2F;code&gt; has many confusing and poorly documented features in the &lt;a href=&quot;https:&#x2F;&#x2F;api.drupal.org&#x2F;api&#x2F;drupal&#x2F;developer!hooks!core.php&#x2F;function&#x2F;hook_menu&#x2F;6.x&quot;&gt;very lengthy api documentation&lt;&#x2F;a&gt;. I&#x27;m going to walk you through some rather simple steps to using &lt;code&gt;hook_menu()&lt;&#x2F;code&gt; to create public-facing menu items. I won&#x27;t get into the complexities of the routing system.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-would-we-want-to-use-this&quot;&gt;Why Would We Want to Use This?&lt;&#x2F;h2&gt;
&lt;p&gt;Since it&#x27;s so easy to create menu items inside the admin pages, why would you ever want to create a custom menu item in code? &lt;strong&gt;Any time you want to create a new path that links to custom functionality&lt;&#x2F;strong&gt;, you&#x27;ll want to reach for &lt;code&gt;hook_menu()&lt;&#x2F;code&gt;. Drupal does a great job of handling url aliases to nodes and other entities (taxonomy, users, etc), but the admin forms don&#x27;t allow you to trigger custom PHP code at an arbitrary path.&lt;&#x2F;p&gt;
&lt;p&gt;An additional benefit that I&#x27;ll touch on briefly is &lt;strong&gt;access control&lt;&#x2F;strong&gt;. This hook allows you to specify what permissions a given path will require of the user. So, any time you need really custom roles or permissions, consider using hook_permission() (aka hook_perm in Drupal 6) to create a new permission. Then read the api documentation above, specifically the &amp;quot;access arguments&amp;quot; section. I&#x27;ll leave this as an exercise for the reader.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;starting-with-the-basics&quot;&gt;Starting with the Basics&lt;&#x2F;h2&gt;
&lt;p&gt;This hook will only be detected by Drupal inside of a module. You can&#x27;t put it in template.php. &lt;&#x2F;p&gt;
&lt;p&gt;Also, it&#x27;s important to know how to trigger this function, since Drupal only runs it rarely. In Drupal 6, you&#x27;ll have to visit the modules page. In Drupal 7, it seems to be enough to clear all caches (you may have to run cron as well). This is important because it&#x27;s to be fooled into thinking that your changes to hook_menu were incorrectly done. One commenter suggested &lt;a href=&quot;https:&#x2F;&#x2F;api.drupal.org&#x2F;comment&#x2F;5129#comment-5129&quot;&gt;a technique for automatically refreshing hook_menu during development.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I should also note that it&#x27;s good to look at the admin menu page periodically to make sure that your changes worked. In drupal 7, you can find the list of menus at &#x27;admin&#x2F;structure&#x2F;menu&#x27;. In Drupal 6, check out &#x27;admin&#x2F;build&#x2F;menu&#x27;. &lt;&#x2F;p&gt;
&lt;h2 id=&quot;getting-our-hands-dirty-hook-menu&quot;&gt;Getting our hands dirty &lt;code&gt;hook_menu()&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s talk about the function signature. Don&#x27;t accept any arguments into your function. Do return an array, which is by convention named &lt;code&gt;$items&lt;&#x2F;code&gt;. The outline of the function will look like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;mymodule_menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() {
    $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is correct, but won&#x27;t do anything yet.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;creating-menu-items&quot;&gt;Creating Menu Items&lt;&#x2F;h3&gt;
&lt;p&gt;To actually create some menu items, we&#x27;ll need to set the paths for the menu items as keys in the &lt;code&gt;$items&lt;&#x2F;code&gt; array. Here&#x27;s a simple example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;mymodule_menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() {
    $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();

    $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;] = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The key line above is &lt;code&gt;$items[&#x27;status&#x27;] = array()&lt;&#x2F;code&gt;. The array key &#x27;status&#x27; tells Drupal to listen for requests at that path or route. (I use the terms &amp;quot;path&amp;quot; and &amp;quot;route&amp;quot; interchangeably throughout this article.) We&#x27;ll need to fill in some more code to tell Drupal how to respond to requests on that route:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;mymodule_menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() {
    $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();

    $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;] = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
        &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;My Status Page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;,
        &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;menu_name&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;main-menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;,
        &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;page callback&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;mymodule_status_page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;,
        &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;access arguments&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;access content&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;),
    );

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The nested array takes several different keys. This is where the online documentation gets really hard to read. Some keys are incompatible or nonsensical with other keys, and other keys are required but only in certain situations. The above code is one combination of keys that will work to create a new top level menu item in the Main Menu labelled &#x27;My Status Page&#x27;. Here&#x27;s a detailed explanation:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;title&lt;&#x2F;strong&gt; - This key allows you to set the text shown to the user on the menu&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;menu_name&lt;&#x2F;strong&gt; - Just what it sounds like. If you omit this, Drupal defaults to the Navigation menu which is only shown to authenticated users. Set this key to whatever menu you want the item to appear in.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;access arguments&lt;&#x2F;strong&gt; - This array is passed to the function &lt;code&gt;user_access()&lt;&#x2F;code&gt; to check if the current user has all those permissions. I always use &lt;code&gt;access content&lt;&#x2F;code&gt; so that if the site content needs to be put behind a paywall, or otherwise hidden later on, it will default to whatever the rest of the site content is set to.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;page-callback-functions&quot;&gt;Page Callback Functions&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;&#x27;page callback&#x27;&lt;&#x2F;code&gt; specifies a callback function name. So, I would need to write a function mymodule_status_page(). If your custom menu item is outputting html, you&#x27;ll want to eventually output using the &lt;code&gt;theme(&#x27;page&#x27;, ...)&lt;&#x2F;code&gt; function. For example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;mymodule_status_page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() {
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Do custom stuff here
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;html &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &amp;#39;&amp;#39;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;Put the final markup in $html
 
    &#x2F;&#x2F; theme(&amp;#39;page&amp;#39;) will wrap your output with a
    &#x2F;&#x2F; page in the create theme (admin or public).
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;theme&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;, $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;html&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;hierarchy-taking-it-to-the-next-level&quot;&gt;Hierarchy: Taking it to the next level&lt;&#x2F;h2&gt;
&lt;p&gt;What if we wanted to nest a menu item? There are two ways of doing this. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-automatic-nesting-based-on-route&quot;&gt;1. Automatic Nesting Based on Route&lt;&#x2F;h3&gt;
&lt;p&gt;By default, Drupal will look at the registered paths in the menu&#x2F;router system, and it will organize them hierarchically. So, if we wanted to add a subpage underneath our &#x27;status&#x27; route (defined above), we could create a new route at &#x27;status&#x2F;us-east-1&#x27;. Here&#x27;s the code we&#x27;d need to use to do that:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;mymodule_menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() {
    $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; &amp;quot;Parent&amp;quot; route
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;] = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; add the keys here
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; &amp;quot;Child&amp;quot; route
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;status&#x2F;us-east-1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;] = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; add keys here
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;ve omitted the keys (&#x27;title&#x27;, etc) for clarity&#x27;s sake, but all the keys on each route work just as we discussed above. &lt;&#x2F;p&gt;
&lt;p&gt;I appreciate the fact that adding routes in this manner will work seamlessly with custom menu items created on menu admin form. Neat! For more examples of nesting using this method, I do recommend checking out the &lt;a href=&quot;https:&#x2F;&#x2F;api.drupal.org&#x2F;api&#x2F;drupal&#x2F;developer!hooks!core.php&#x2F;function&#x2F;hook_menu&#x2F;6.x&quot;&gt;api documentation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-explicitly-specifying-a-parent-menu-item&quot;&gt;2. Explicitly Specifying a Parent Menu Item&lt;&#x2F;h3&gt;
&lt;p&gt;What if you wanted to make &#x27;status&#x2F;us-east-1&#x27; the parent of &#x27;status&#x27; in the menu? Unfortunately, there&#x27;s no safe way to do that across browsers other than manually dragging the menu items around in the admin form. The reason we can&#x27;t do that safely is that we don&#x27;t a reliable to figure out what the menu id (called &lt;code&gt;mlid&lt;&#x2F;code&gt;) is in the database until &lt;em&gt;after&lt;&#x2F;em&gt; &lt;code&gt;hook_menu()&lt;&#x2F;code&gt; runs. This is a really unfortunate problem with the existing system that lumps menu and router systems together.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, we can rearrange menu items by specifying the parent menu id (called the &lt;code&gt;plid&lt;&#x2F;code&gt;) for known items. The &lt;code&gt;plid&lt;&#x2F;code&gt; number will almost certainly be specific to the database you&#x27;re working on, so I wouldn&#x27;t use it for a contrib module. However, this is perfectly acceptable for your own freelance or agency custom module development.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;here-s-how-it-works&quot;&gt;Here&#x27;s how it works:&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;Create the child menu item in &lt;code&gt;$items&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Find the parent menu item we want to attach the child to&lt;&#x2F;li&gt;
&lt;li&gt;Find the &#x27;mlid&#x27; value of the parent menu item&lt;&#x2F;li&gt;
&lt;li&gt;Add a new key to the child menu item called &#x27;plid&#x27; and set the value you found in step 3.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The tricky step here is finding the mlid value of the parent. The simplest way to do that is to use the admin form page and mouse over the parent menu item edit link. Use that number at the end of url:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;2014-01-14-mlid-hint.png&quot; alt=&quot;Finding the mlid on the Drupal admin forms for menu editing&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what that code looks like:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;mymodule_menu&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;() {
    $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; &amp;quot;Child&amp;quot; route
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;status&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;] = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; add the other keys here
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;plid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;218&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; mlid of the parent
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);

    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</content>
	</entry>
	<entry xml:lang="en">
		<title>Considerations for Multisite Drupal</title>
		<published>2013-12-28T00:00:00+00:00</published>
		<updated>2013-12-28T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/considerations-for-multisite-drupal/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/considerations-for-multisite-drupal/</id>
		<content type="html">&lt;p&gt;We&#x27;ve been using &lt;a href=&quot;https:&#x2F;&#x2F;www.drupal.org&#x2F;project&#x2F;domain&quot;&gt;the Domain Access the module&lt;&#x2F;a&gt; at my day job for about 5 years with Drupal 6. Recently, we&#x27;ve decided Domain Access is not the right solution for our use case.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;domain-access&quot;&gt;Domain Access&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;strengths&quot;&gt;Strengths&lt;&#x2F;h3&gt;
&lt;p&gt;According to the module&#x27;s project page, Domain Access (emphasis mine)&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;provides tools for running a group of &lt;strong&gt;affiliated sites&lt;&#x2F;strong&gt; from one Drupal installation and a single shared database. The module allows you to share users, &lt;strong&gt;content,&lt;&#x2F;strong&gt; and configurations.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The problem this module aims to solve is allowing multiple domains to have &lt;strong&gt;identical content&lt;&#x2F;strong&gt;. If you&#x27;re trying to build multiple sites with &lt;strong&gt;substantial amounts of identical content&lt;&#x2F;strong&gt;, definitely use the Domain Access module. It&#x27;s fantastic at making the very same information available on different subdomains (one.example.com and two.example.com) and different top level domains as well (one.com and two.com). One of the best features of Domain Access is the ability to share users and passwords across different sites using the same Drupal authentication system you already know. Awesome!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pitfalls&quot;&gt;Pitfalls&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Tight Coupling of Dependencies&lt;&#x2F;strong&gt; If you&#x27;re using Domain Access, the same modules are powering your site. This could be a good thing or a bad thing depending on your perspective. From my point of view, this is more of a problem than a solution. For example, imagine a new version of Views comes out and you have twenty views with heavy customization on each domain of 5 domains. You have to be sure that upgrading the Views module won&#x27;t break 100 different views before you deploy the new module. Personally, I&#x27;d rather test only 20 views at a time rather than 100.&lt;&#x2F;p&gt;
&lt;p&gt;Any big change to any of the sites (especially contrib modules) means you should really be checking the admin forms and a good sampling of urls and functionality across each domain. It sounds like less work to maintain only one Drupal instance that powers multiple websites. In my experience, it&#x27;s almost always more work to force one Drupal instance to work across a spectrum of different sites, each with different requirements and stakeholders. It ends up being less work to ship any given piece of code when each site is it&#x27;s own island.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;SEO Snafus&lt;&#x2F;strong&gt; In the six months I&#x27;ve been at my current job, I&#x27;ve gotten numerous complaints of nodes being indexed at the wrong domain. Domain Access only checks for &lt;em&gt;permission&lt;&#x2F;em&gt; of the current user for a given path. This means that you can&#x27;t have two different nodes at the same path at different domains. Each node must have a unique path across all domains. Additionally, if a user mistakenly accesses a node that exists at a different subdomain, they will get a 403 error (access denied) instead of 404 error (not found). Each site builder must be trained on how to use set the appropriate the domain access settings. If nodes are configured improperly, Google can (and will!) index the wrong domain for that node. Many times, certain content types are only properly themed for one domain, and when they are indexed at a different domain, the content looks horrible.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Views and Node Access&lt;&#x2F;strong&gt; Unfortunately, Views will mostly respect the user access settings of a given node. At least in Drupal 6 and probably Drupal 7, this means that you can&#x27;t easily create views with teasers of content from another domain unless that content is accessible on the same domain as the view. For instance, at our main corporate site we have a blog teaser. Unfortunately, we couldn&#x27;t create a view because each of our blog posts was only allowed on our blog domain. As a workaround, we&#x27;ve created RSS feeds using views on the blog site, then ingested that feed on the corporate site in many instances. For other situations, we&#x27;ve abandoned Views and written SQL directly.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;SEO Penalties&lt;&#x2F;strong&gt; Additionally, when using this module correctly, major &lt;a href=&quot;https:&#x2F;&#x2F;support.google.com&#x2F;webmasters&#x2F;answer&#x2F;66359?hl=en&quot;&gt;search engines will sometimes penalize urls that showing identical content&lt;&#x2F;a&gt; at different domains.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;use-separate-drupal-databases&quot;&gt;Use Separate Drupal Databases&lt;&#x2F;h2&gt;
&lt;p&gt;Another way to approach a multisite situation is to create new database on a MySQL server, and create another &lt;code&gt;settings.php&lt;&#x2F;code&gt; file. The advantage of this technique is that you can share module code across different Drupal instances, but you&#x27;ll avoid all the issues with duplicate content showing up on the wrong domains. This is a huge win, and could be a much better solution for your multisite needs. However, you&#x27;ll still run into all the &amp;quot;tightly coupled dependencies&amp;quot; issues that arise whenever you need to upgrade modules or Drupal core.&lt;&#x2F;p&gt;
&lt;p&gt;The biggest problem with this approach to multisite Drupal is that you can&#x27;t share users and passwords between different Drupal instances. This is actually a nontrivial problem — you don&#x27;t want to have different usernames and passwords across affiliated sites. Just imagine if you had a separate account for every stack exchange affiliate! There are several solutions to this problem, but they all involve various trade offs. I plan to discuss this issue more in a future post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;don-t-do-multisite&quot;&gt;Don&#x27;t Do Multisite&lt;&#x2F;h2&gt;
&lt;p&gt;This is my preferred solution. &lt;strong&gt;Each domain that is (or could reasonably become) different should have it&#x27;s own fully independent Drupal instance, code and data.&lt;&#x2F;strong&gt; This approach means that you don&#x27;t have to review every site after every change, and you won&#x27;t have problems with content being indexed on the wrong domain. I would guess that for most organizations that want to use multiple domains or subdomains, they do want unique content on unique domains. (It&#x27;s actually hard to imagine a situation where you&#x27;d want the &lt;em&gt;same&lt;&#x2F;em&gt; content on &lt;em&gt;different&lt;&#x2F;em&gt; domains).&lt;&#x2F;p&gt;
&lt;p&gt;You also avoid the tightly coupled code issues that arise for the separate settings.php file. The &amp;quot;downside&amp;quot; is that you&#x27;ll have to upgrade each site individually. However, this is feature not a bug in my mind. As I mentioned before, I&#x27;d love to do &lt;em&gt;less&lt;&#x2F;em&gt; testing and manual QA for each code deployment. I don&#x27;t mind spending a little extra time testing my changes in exchange for greater confidence that my changes won&#x27;t break a totally different site.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Best Practices for Translation in Drupal</title>
		<published>2013-12-13T00:00:00+00:00</published>
		<updated>2013-12-13T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/best-practices-for-translation-in-drupal/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/best-practices-for-translation-in-drupal/</id>
		<content type="html">&lt;p&gt;One of the biggest reasons my company decided to use Drupal for their &lt;a href=&quot;http:&#x2F;&#x2F;www.brightcove.com&#x2F;&quot;&gt;marketing website&lt;&#x2F;a&gt; is the capacity to translate and &amp;quot;localize&amp;quot; content. Drupal has a series of APIs going back at least to Drupal 4.6 that make it possible to have different paths for various pieces content. Drupal also has the ability to import and export translations of strings stored in code as well as content stored in nodes, taxonomy terms, and more. Most translation vendors can accommodate the exported content into their software and ship back a &amp;quot;localized&amp;quot; version of that content.&lt;&#x2F;p&gt;
&lt;p&gt;Recently, as I&#x27;ve been involved in this process, I&#x27;ve discovered several pitfalls and how best to avoid them. I&#x27;ll be focusing this post on Drupal 6 since that&#x27;s what I use most often, but I&#x27;ll also try to mention some things about Drupal 7 as we go. I won&#x27;t tell you &lt;a href=&quot;https:&#x2F;&#x2F;www.drupal.org&#x2F;node&#x2F;275705&quot;&gt;how to get started localizing your website&lt;&#x2F;a&gt; -- that&#x27;s a whole book in and of itself. These are more guidelines for how to minimize friction between themers, site builders, and translators.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;block-level-elements-and-the-t-function&quot;&gt;Block Level Elements and the t() function&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;api.drupal.org&#x2F;api&#x2F;drupal&#x2F;includes!common.inc&#x2F;function&#x2F;t&#x2F;6.x&quot;&gt;t() function&lt;&#x2F;a&gt; takes English strings that are used in PHP and exposes them to Drupal for translation. Drupal takes care of the hard part of figuring out which language to display on the current page, but Drupal won&#x27;t know to translate your string unless you&#x27;ve first wrapped it in the t(). The &lt;a href=&quot;https:&#x2F;&#x2F;api.drupal.org&#x2F;api&#x2F;drupal&#x2F;includes!common.inc&#x2F;function&#x2F;t&#x2F;6.x&quot;&gt;API documentation for t()&lt;&#x2F;a&gt; has lots of great examples of correct and incorrect usage of t(). The bottom line is inline elements should go inside t() and block level elements outside.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;lt;!-- Correct! --&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Your new &amp;lt;strong&amp;gt;BFF&amp;lt;&#x2F;strong&amp;gt; in web development&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;lt;!-- Incorrect --&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;lt;p&amp;gt;Your new &amp;lt;strong&amp;gt;BFF&amp;lt;&#x2F;strong&amp;gt; in web development&amp;lt;&#x2F;p&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that paragraph tags are block level elements in CSS, and therefore should not be included in t(). However, inline elements like &lt;code&gt;a&lt;&#x2F;code&gt;, &lt;code&gt;strong&lt;&#x2F;code&gt;, &lt;code&gt;em&lt;&#x2F;code&gt;, and &lt;code&gt;br&lt;&#x2F;code&gt; should definitely go inside t().&lt;&#x2F;p&gt;
&lt;p&gt;NOTE: There&#x27;s lots of ways to get translation strings out of Drupal and expose English text for translation inside Drupal. You only need to use &lt;code&gt;t()&lt;&#x2F;code&gt; for English text inside PHP code. You don&#x27;t need it inside nodes (for instance). More on this later.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;escaping-values-inside-t&quot;&gt;Escaping Values Inside t()&lt;&#x2F;h2&gt;
&lt;p&gt;Often times, you&#x27;ll want to pass a value that shouldn&#x27;t be translated into &lt;code&gt;t()&lt;&#x2F;code&gt;, such as a URL, the number of users currently signed in, etc. However, it&#x27;s &lt;strong&gt;really, really important NOT to get fancy&lt;&#x2F;strong&gt;. Each use of &lt;code&gt;t()&lt;&#x2F;code&gt; creates a separate string for a translator to figure out. You&#x27;ll often have to make judgment calls about how to break up the strings into meaningful bits. Always, step back and ask yourself, &amp;quot;What does the translator need to see to understand this text the way the use will see it?&amp;quot;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;example-1-don-t-use-unnecessary-plurals&quot;&gt;Example 1: Don&#x27;t Use Unnecessary Plurals&lt;&#x2F;h3&gt;
&lt;p&gt;Again here&#x27;s some examples:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Correct
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Providing !visitors visitors with outrageously awesome Drupal advice&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;,
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;!visitors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;num_visitors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)
);

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Yikes! Confusing
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Providing !visitors visitors with outrageously awesome Drupal advice&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;,
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;!visitors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;format_plural&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;num_visitors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;1 visitor&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;, &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;@count visitors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;))
);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s unpack that a bit. You can tell &lt;code&gt;t()&lt;&#x2F;code&gt; to insert variables into your strings using a special escape syntax. Use ! or @ or % to tell Drupal where to insert your variable. Then pass &lt;code&gt;t()&lt;&#x2F;code&gt; an array of values. So, the above examples are just inserting the value of &lt;code&gt;$num_visitors&lt;&#x2F;code&gt; into each string above.&lt;&#x2F;p&gt;
&lt;p&gt;Technically speaking, the above &amp;quot;confusing&amp;quot; example is not wrong. However, the translator won&#x27;t see the plural translation strings (&#x27;1 visitor&#x27; and &#x27;@count visitors&#x27;) strings alongside of the main string. The first string is better because it keeps the whole sentence in one single string that the translator can figure out much more easily.&lt;&#x2F;p&gt;
&lt;p&gt;There may be situations where you could one or multiple numbers inside &lt;code&gt;t()&lt;&#x2F;code&gt; variables. It&#x27;s perfectly appropriate to use the &lt;code&gt;format_plural&lt;&#x2F;code&gt; function in those situations, but you may want to give your translation vendor a heads up about that string. I&#x27;ve seen translators consistently unable to figure out this sort of problem — and I don&#x27;t blame them. It&#x27;s our responsibility to make sure translators eceive content they can understand. Going the extra for your translators will save you time and money by not having to send back strings for re-translation over and over again.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;example-2-don-t-use-abstraction&quot;&gt;Example 2: Don&#x27;t Use Abstraction&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;ve seen some programmers try to separate repeated strings into different &lt;code&gt;t()&lt;&#x2F;code&gt; invokations. Let&#x27;s look at an example HTML output in English:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;lt;!-- This how the final HTML should look in English --&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;Bryce Fisher-Fleig - Astounding Drupal Insights&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;Bryce Fisher-Fleig - Jaw Dropping Coding Skillz&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;Bryce Fisher-Fleig - Oh, snap! He did it again&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;re working in PHP and we see a pattern, so why not DRY up the code a little bit? So, trying to use &amp;quot;abstraction&amp;quot; on these strings might lead to something like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;lt;!-- Do NOT do this at home, kids! --&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Bryce Fisher-Fleig - !description&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;!description&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Astounding Drupal Insights&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;))); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Bryce Fisher-Fleig - !description&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;!description&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Jaw Dropping Coding Skillz&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;))); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Bryce Fisher-Fleig - !description&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;!description&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Oh, snap! He did it again&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;))); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;This is an antipattern because the translator might need to reorder these strings&lt;&#x2F;strong&gt;, or they might translate the text different having known the strings appear next to each other. Here&#x27;s an example of how that might need to appear in Japanese:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;lt;!-- Thank you google translate! --&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;驚異Drupalの洞察 - Bryce Fisher-Fleig&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;顎落としたりするスキルコーディング - Bryce Fisher-Fleig&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;ああ、スナップ！彼は再びそれをやった - Bryce Fisher-Fleig&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you break the string into pieces, translators won&#x27;t be able to see that the strings belong together and you&#x27;ll have wasted your money on the translation effort. &lt;strong&gt;Always keep strings together for the translator just like you would for the end user&lt;&#x2F;strong&gt;. I promise you the translators will be thoroughly confused if you try to separate the repeated strings. Let the experts make the call on whether or not a given phrase should be translated the same way in different contexts. Just keep the strings together as much as possible so the translators can have at least full sentence context, like so:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;lt;!-- This is the right way --&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;BryceAdamFisher - Astounding Drupal Insights&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;BryceAdamFisher - Jaw Dropping Coding Skillz&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;BryceAdamFisher - Oh, snap! He did it again&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;preparing-to-export-strings-from-t&quot;&gt;Preparing to Export Strings from &lt;code&gt;t()&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If you do use escaped values inside &lt;code&gt;t()&lt;&#x2F;code&gt;, you&#x27;ll need to notify the translators that words starting with ! @ or % need to left as is because they will be replaced by strings automatically inside Drupal. Each time you start translating fresh content, it might be wise to notify your vendor just in case they&#x27;ve forgotten or changed some settings in their translation software.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;d also highly recommend grooming through your templates for fancy usage of &lt;code&gt;t()&lt;&#x2F;code&gt; that could be simplified or made clearer. Doing so will save you time and money, and make the whole process smoother.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;other-ways-to-make-strings-translatable&quot;&gt;Other Ways to Make Strings Translatable&lt;&#x2F;h2&gt;
&lt;p&gt;In Drupal 6, using the the &lt;a href=&quot;https:&#x2F;&#x2F;www.drupal.org&#x2F;project&#x2F;i18n&quot;&gt;i18n module&lt;&#x2F;a&gt; and related modules allows you to translate blocks, taxonomy terms, node titles, node url aliases (which is awesome!), and node body fields. However, there&#x27;s no good way to translate custom CCK fields. Menus are easier to simply create multiple versions of that are only visible for a specific language.&lt;&#x2F;p&gt;
&lt;p&gt;In Drupal 7, you can also leverage the &lt;a href=&quot;https:&#x2F;&#x2F;www.drupal.org&#x2F;project&#x2F;entity_translation&quot;&gt;entity translation module&lt;&#x2F;a&gt; to translate custom fields.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;content-lifecycle&quot;&gt;Content Lifecycle&lt;&#x2F;h2&gt;
&lt;p&gt;One final best practice I want to make special note of is &lt;strong&gt;never manually edit translation files&lt;&#x2F;strong&gt;. Your project manager wants to ship code (and so do you!) but if you playing God with your Korean text, bad things will happen. Never never ever edit the import or export files directly. These files are designed to be used in conjunction with automatic processes so you don&#x27;t have to mess with translation. That&#x27;s also why you&#x27;re paying good money to language experts. So why would you screw it up at the last minute? Do your due diligence to prepare your templates by properly using the &lt;code&gt;t()&lt;&#x2F;code&gt; in clear, unambiguous ways, and stay out of the import&#x2F;export files. You&#x27;ll thank yourself.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Quick Drupal - Lessons Learned While Whipping Up a Fresh Drupal Instance</title>
		<published>2013-11-29T00:00:00+00:00</published>
		<updated>2013-11-29T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/quick-drupal-lessons-learned-while-whipping-up-a-fresh-drupal-instance/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/quick-drupal-lessons-learned-while-whipping-up-a-fresh-drupal-instance/</id>
		<content type="html">&lt;p&gt;I&#x27;ve recently migrated to Ubuntu 12.04 from Windows, and I ran into some issues getting the drush core-quick-drupal command. As a quick refresher, the &amp;quot;quick drupal&amp;quot; command will download, install, and even login to a new drupal instance on your local machine. It&#x27;s really hand for debugging and also contributing patches back to drupal.org because you eliminate any side effects from bizarre customizations. Coming from a Windows background there were quite a few hurdles I had to figure out -- so I thought I&#x27;d record my experiences here for other poor souls trying to use quick drupal on Ubuntu. Some of these tips will doubtless be helpful on Mac or Windows as well.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problem-old-drush-installed&quot;&gt;Problem - Old Drush Installed&lt;&#x2F;h2&gt;
&lt;p&gt;Ubuntu uses a package management tool called &amp;quot;apt-get&amp;quot; to install, update, and remove applications from the operating systems. If you&#x27;ve ever used composer for PHP, or npm for NodeJS, it&#x27;s exactly the same, and it&#x27;s my favorite feature of linuxy operating systems over Mac and Windows. Conveniently, apt-get has a package for drush!&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo apt-get install drush
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Unfortunately, the apt-get version of Drush is version 4.x, whereas the stable version of Drush is 5.x. Also, the quick drupal command we&#x27;re insterested in is only added to drush starting somewhere in the 5.x branch. However, drush has it&#x27;s own self-update command. Hooray!&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; drush self-update&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -y
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Phew! Now if you run &lt;code&gt;drush help&lt;&#x2F;code&gt; in the terminal, you&#x27;ll see that the command &amp;quot;core-quick-drupal&amp;quot; listed near the top. &lt;&#x2F;p&gt;
&lt;h2 id=&quot;problem-rtfm&quot;&gt;Problem - RTFM&lt;&#x2F;h2&gt;
&lt;p&gt;I &lt;em&gt;thought&lt;&#x2F;em&gt; I was an experienced drush ninja, but it turns out I hadn&#x27;t fully read the documentation carefully for core-quick-drupal command. One reason I love the quick drupal command is that it doesn&#x27;t require you to setup a mysql database (you can use SQLite which is much simpler for testing patches) and you don&#x27;t need to configure Apache (more on that later). You just type this one command and everything magically works.&lt;&#x2F;p&gt;
&lt;p&gt;To use quick drupal as I had intended, you actually need to use the first optional parameter. So, the command will look like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; drush core-quick-drupal patches&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -y
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notice &amp;quot;patches&amp;quot; in the command above. &amp;quot;patches&amp;quot; is the site name parameter, and you &lt;strong&gt;must&lt;&#x2F;strong&gt; specify this parameter or else drush will try to use mysql for datastore, and if it fails you&#x27;ll have all kinds of weird errors. The moral of the story here is to always &amp;quot;read the fucking manual&amp;quot; as the old adage goes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problem-sqlite-not-installed&quot;&gt;Problem - SQLite Not Installed&lt;&#x2F;h2&gt;
&lt;p&gt;Eventually, I realized that SQLite does not come pre-installed on Ubuntu 12.04 (at least, not in the Desktop distro that I downloaded). I also found some blogs that mentioned PHP5 requires sqlite v3 to work and the php5 sqlite connector. I use apt-get to install these dependencies:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo apt-get install sqlite3 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo apt-get install php5-sqlite3
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you try the quick drupal command again, you should start to get helpful error messages at this point.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;problem-required-php-extensions-missing&quot;&gt;Problem - Required PHP Extensions Missing&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;ve followed along with me so far, you can probably figure the rest of this out on your own just fine. &lt;&#x2F;p&gt;
&lt;p&gt;However, this next hurdle blocked me for a while because the error message in the terminal had html around it. With my particular setup, I had use apt-get to install php on my machine but certain extensions required by Drupal were not installed as part of apt-get command. In my case, I found that the &amp;quot;gd&amp;quot; graphics library was not installed. Fortunately, a compatible version of gd for php5 can be easily installed:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo apt-get install php5-gd
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;use-a-compatible-php-binary&quot;&gt;Use a Compatible PHP Binary&lt;&#x2F;h2&gt;
&lt;p&gt;The quick drupal command was intended to be used with PHP 5.4 because this version of PHP has an embedded web server that obviates the need for Apache or Nginx (or whatever). It&#x27;s very easy to get PHP 5.4 with a full WAMPP stack on Windows using Apache Friends&#x27; XAMPP installer. I highly recommend that approach since you won&#x27;t have to follow any of the steps in this tutorial to get up and running with Drupal.&lt;&#x2F;p&gt;
&lt;p&gt;Ubuntu&#x27;s apt-get only has PHP 5.3 which doesn&#x27;t have the php web server built-in. Furthermore, drush core-quick-drupal doesn&#x27;t like the cli flavor of PHP 5.3 either. So, unless you want to compile PHP and all the necessary extensions from source, I recommend you install the PHP cgi binary:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo apt-get install php-cgi
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;emulating-the-web-server-on-php-5-3&quot;&gt;Emulating the Web Server on PHP 5.3&lt;&#x2F;h3&gt;
&lt;p&gt;If you try to run quick drupal now, Drush has a fantastic error message that tells you exactly what to do. You&#x27;ll need to download a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;youngj&#x2F;httpserver&#x2F;tarball&#x2F;41dd2b7160b8cbd25d7b5383e3ffc6d8a9a59478&quot;&gt;library from github that runs a pure php httpserver&lt;&#x2F;a&gt;. This is only necessary for php 5.3. You&#x27;ll need to unpack this tarball in the &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;drush&#x2F;lib&lt;&#x2F;code&gt; directory. When you&#x27;re done, make sure that this file exists: &lt;code&gt;&#x2F;usr&#x2F;share&#x2F;drush&#x2F;lib&#x2F;youngj-httpserver-41dd2b7&#x2F;httpserver.php&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Sadly, quick drupal still won&#x27;t work without one more component.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setup-a-local-email-server&quot;&gt;Setup A Local Email Server&lt;&#x2F;h2&gt;
&lt;p&gt;Quick Drupal is starting to not be so quick, you&#x27;re probably thinking. But, it will be once you get this command working. You&#x27;re almost there! The only thing missing at this point is a working email server. Drupal needs to be send out an administrator email when you start the quick drupal instance. There&#x27;s probably a way around this step, but it&#x27;s actually easier than I had thought.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dummy-mail-for-windows&quot;&gt;Dummy Mail for Windows&lt;&#x2F;h3&gt;
&lt;p&gt;If you&#x27;re running Windows, just use &lt;a href=&quot;https:&#x2F;&#x2F;smtp4dev.codeplex.com&#x2F;&quot;&gt;SMTP 4 Dev a dummy SMTP server&lt;&#x2F;a&gt;. It&#x27;s so easy... you&#x27;ll thank yourself a millions times for using this program.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-real-email-server-for-ubuntu&quot;&gt;A &amp;quot;Real&amp;quot; Email Server for Ubuntu&lt;&#x2F;h3&gt;
&lt;p&gt;On Ubuntu, we&#x27;re going to install postfix and mailutils, and then we&#x27;ll configure them. There&#x27;s a great &lt;a href=&quot;http:&#x2F;&#x2F;serverfault.com&#x2F;questions&#x2F;119105&#x2F;setup-ubuntu-server-to-send-mail&quot;&gt;stackoverflow that explains postfix setup nicely&lt;&#x2F;a&gt;, but I&#x27;ll explain how I did it slightly differently. &lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo apt-get install postfix
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;During the install screen in the terminal, I choose &amp;quot;local server&amp;quot; (the last option). This means that postfix will only deliver emails locally to the current machine. The next screen will ask you for a Fully Qualified Doman Name. This doesn&#x27;t matter -- I entered localhost and it worked fine. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;checking-email-in-the-terminal&quot;&gt;Checking Email in the Terminal&lt;&#x2F;h3&gt;
&lt;p&gt;Next you&#x27;ll need to install the command line mail client:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo apt-get install mailutils
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Try sending yourself a test email now like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; mail&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -s &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Testing&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot; root@localhost
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Cc: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;ENTER&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;[CTRL-D]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Pressing CTRL + D ends the message body and sends the message. You should be able to see that message was sent (&lt;code&gt;Null message body; hope that&#x27;s ok&lt;&#x2F;code&gt;). You should be good to go!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tell-php-how-to-use-postfix-to-email-you-the-admin-password&quot;&gt;Tell PHP How to Use Postfix to Email You the Admin Password&lt;&#x2F;h3&gt;
&lt;p&gt;Then under &#x2F;etc&#x2F;php5&#x2F;conf.d create a file (e.g. mailconfig.ini) with these contents (note you&#x27;ll need sudo permissions to save the file in this directory):&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sendmail_from &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;quot;root@localhost&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sendmail_path &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;quot;&#x2F;usr&#x2F;sbin&#x2F;sendmail -t -i -f root@localhost&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;it-works&quot;&gt;It Works!&lt;&#x2F;h2&gt;
&lt;p&gt;Finally, we can now run the quick drupal command...quickly. Here&#x27;s how to do it:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; cd &#x2F;path&#x2F;to&#x2F;where&#x2F;drupal&#x2F;should&#x2F;be
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; drush qd my_drupal&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -y
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A few minutes later, you&#x27;ll have a php server running a Drupal instance on SQLite, and your browser will open and login you in as the admin user. Hooray! Now go make some patches.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Getting to Done Faster with Drush and PHP</title>
		<published>2013-11-22T00:00:00+00:00</published>
		<updated>2013-11-22T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/getting-to-done-faster-with-drush-and-php/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/getting-to-done-faster-with-drush-and-php/</id>
		<content type="html">&lt;p&gt;If you&#x27;re like me, you&#x27;ve been using Drush (&amp;quot;DRUsh SHell&amp;quot;) for a while to make several tasks less tedious: downloading and enabling modules, running database updates, backing up the database, clearing the drupal cache, running drupal cron, and resetting the admin password. Drush does all those things really well for drupal 6 and 7. It just works!&lt;&#x2F;p&gt;
&lt;p&gt;This post is about using Drush as part of your coding process for theming or module development (or even core contributions). Drush and PHP have several commands that I&#x27;ve come to find invaluable while I write code. These commands let you run small snippets without waiting for a whole page to load in the browser. Simply pop open your shell, and start typing. This can be invaluable to make sure that the code behaves how you think it will. This makes your code faster to write and less prone to error because you&#x27;ll already know what the code is doing better.&lt;&#x2F;p&gt;
&lt;p&gt;One final argument for using these techniques is isolation. It&#x27;s often hard to trigger exactly the piece of code that you want to understand. All these commands allow you focus on a specific piece of code without the distractions and complications of a whole site.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;command-1-evaluating-php-snippets-in-drupal-drush-ev&quot;&gt;Command 1: Evaluating PHP snippets in Drupal &lt;code&gt;drush ev&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This command evaluates a single line of PHP code in the context of drupal. I use this all the time to test that various Drupal API functions work as I expect. Here&#x27;s a quick example. Let&#x27;s say you&#x27;re not sure if the &lt;code&gt;l()&lt;&#x2F;code&gt; function will return the URL or the anchor tag inside your template function. Here&#x27;s what do:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; cd &#x2F;path&#x2F;to&#x2F;drupal
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; drush ev &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;print l(&amp;#39;node&#x2F;1&amp;#39;);&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;http:&#x2F;&#x2F;example.com&#x2F;node
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Great! We can now see how this particular function will work without doing a bunch of other stuff that takes 10-15 seconds. We now know exactly what to put inside our template file. &lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s another simple example of &lt;code&gt;drush ev&lt;&#x2F;code&gt;. Let&#x27;s say we&#x27;ve written a helper function in our custom module that takes a nid and returns a field value. However, you wrote this function last month you&#x27;re sure if the return value is an object or an array or a string.  Let&#x27;s find out:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; cd &#x2F;path&#x2F;to&#x2F;drupal
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; drush ev &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;var_dump(my_module_helper(1));&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(1) { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; =&amp;gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;hello drush&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You&#x27;ve just saved yourself several minutes digging through your own code. Well done! There&#x27;s a million ways to use this code. I often use &lt;code&gt;drush ev&lt;&#x2F;code&gt; to test each command I&#x27;m interesting in while implementing a function for the first time. This prevents me from running into surprises 99% of the time and has made me much more productive.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;caveats-with-drush-ev&quot;&gt;Caveats with &lt;code&gt;drush ev&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Drush won&#x27;t print anything to the shell when the command ends, so you&#x27;ll need to use &lt;code&gt;print_r()&lt;&#x2F;code&gt;, &lt;code&gt;print&lt;&#x2F;code&gt;, &lt;code&gt;echo&lt;&#x2F;code&gt;, or &lt;code&gt;var_dump()&lt;&#x2F;code&gt; to see the result of the command in the shell.&lt;&#x2F;p&gt;
&lt;p&gt;The big limitation of &lt;code&gt;drush ev&lt;&#x2F;code&gt; is that you can only execute one liners. If the code you want to execute can&#x27;t be called in one line, see if the next technique is more appropriate. &lt;&#x2F;p&gt;
&lt;h2 id=&quot;command-2-interactive-php-shell-with-php-a&quot;&gt;Command 2: Interactive php shell with &lt;code&gt;php -a&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This command opens an interactive php shell which is really useful for prototyping a sequence of built-in php commands that can&#x27;t be fit into a one-liner. This is awesome for experimenting with array operations, reflections in PHP, regex functions like &lt;code&gt;preg_match()&lt;&#x2F;code&gt;, or other confusing syntax. Any time you&#x27;re not sure how a php function will work, give this a shot.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;mac-and-linux-examples-of-interactive-php-shell&quot;&gt;Mac and Linux Examples of interactive PHP Shell&lt;&#x2F;h3&gt;
&lt;p&gt;On OSX and Linux, here&#x27;s how this works:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; php&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -a

interactive&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; shell
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt; $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; = serialize(array(1 =&amp;gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;abc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;));
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt; echo $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a:1:{i:1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s:3:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;abc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that you still need to issue some sort of print&#x2F;var_dump command to see the output. However, you can create and manipulate as many variables as you want on as many lines of code as you want.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;windows-example&quot;&gt;Windows Example:&lt;&#x2F;h3&gt;
&lt;p&gt;I no longer have a windows machine at home, but this works a little differently on Windows. Here&#x27;s an example from memory:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-powershell&quot; data-lang=&quot;powershell&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;C:\&amp;gt; php -e

&amp;lt;?php
$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= serialize(array(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;abc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;));
echo $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;CTRL-Z
a:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:{i:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;s:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;:&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;abc&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;}

C:\&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So, the main thing to note is that php on windows will treat whatever you type as a regular php file which requires the use of &lt;?php and ?&gt; to work. Also, php won&#x27;t start interpreting until you press CTRL + Z and ENTER after the closing ?&amp;gt;. So, this is less useful on windows, but still really handy when you need it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;command-3-executing-sql-queries-with-drush-sqlq&quot;&gt;Command 3: Executing SQL Queries with &lt;code&gt;drush sqlq&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;ve ever found yourself typing SQL on the command through the mysql client, it can be a real pain to look up the user name and password just to execute some simple queries. Drush has your back! It uses it&#x27;s magic to execute sql queries in the context of the Drupal database with &lt;code&gt;drush sqlq&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This command is really useful for figuring out sql querys that &lt;strong&gt;change&lt;&#x2F;strong&gt; the database. For instance, if you want to write a module update that unpublishes all nodes of a certain type, you could try out the query using this command. To see the effects, however you&#x27;ll still need to look at the live site directly or use a MySQL gui client like Sequel for Mac or Heidi SQL for Windows.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-word-about-best-practices&quot;&gt;A Word about Best Practices&lt;&#x2F;h2&gt;
&lt;p&gt;The drush commands I&#x27;ve shown you are really powerful. And, as uncle Ben taught us in Spiderman, &amp;quot;with great power comes great responsibility.&amp;quot; &lt;strong&gt;Please do not use these commands on a production website.&lt;&#x2F;strong&gt; These commands are awesome at debugging your problems locally, but they can really mess up your website. They are not &amp;quot;sandboxed&amp;quot; in anyway.&lt;&#x2F;p&gt;
&lt;p&gt;Okay, with that said, go out there and write some great code that works the first time you run it!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Settings.php Hack - Keep Credentials out of Your Code</title>
		<published>2013-11-12T00:00:00+00:00</published>
		<updated>2013-11-12T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/settings-php-hack-keep-credentials-out-of-your-code/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/settings-php-hack-keep-credentials-out-of-your-code/</id>
		<content type="html">&lt;p&gt;Over my three year Drupal career, I&#x27;ve seen several interesting server setups for running Drupal. One technique I&#x27;ve been gravitating toward is storing the environment specific configuration settings &lt;strong&gt;in the environment&lt;&#x2F;strong&gt;. &lt;&#x2F;p&gt;
&lt;p&gt;##Motivation For This Hack&lt;&#x2F;p&gt;
&lt;p&gt;As the web moves more and more into cloud computing, we developers need to get comfortable letting our code run in a new and unforeseen range of contexts. I believe that building our software to depend on environment variables is an excellent way to build more portable code that is flexible enough to thrive in these changing conditions. &lt;&#x2F;p&gt;
&lt;p&gt;Currently as I write this post in November, 2013, I&#x27;ve been running this site on shared hosting through HostGator — not the most exciting choice these days, but it is an affordable option. On many shared hosts including HostGator, databases must be prefixed by your username. However, I like to run my databases using different credentials locally. Also, to setup a staging environment on the same server, you&#x27;d need to create a separate database with separate credentials.&lt;&#x2F;p&gt;
&lt;p&gt;My final argument for this particular technique is transparency. I store the code for several side projects on Github as open source projects, but I really don&#x27;t want the credentials stored there for all to see. By removing credentials from the code, I feel much more confident about using Github this way. Conveniently, Github does not charge for storing open source projects (but they do charge for hosting private repositories).&lt;&#x2F;p&gt;
&lt;p&gt;##Playing with Environment Variables&lt;&#x2F;p&gt;
&lt;p&gt;The trick to getting credentials out of your settings.php file is to use &lt;strong&gt;environment variables&lt;&#x2F;strong&gt;. The shells for Windows, Mac, and Linux are able to store small pieces of information in the environment. It&#x27;s really easy to temporarily set an environmental variable in a bash shell:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; set x=&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;y&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; echo $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;x
y
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here&#x27;s how it looks in a Windows command prompt:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-powershell&quot; data-lang=&quot;powershell&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt; set x=&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;y&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;
&amp;gt; echo %x%
&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;y&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;However, these environment variables will poof out of existence as soon as your restart your computer (or close that terminal session!). In order for Drupal to work, we need a &amp;quot;set it and forget it&amp;quot; approach to these environment variables, so that they persistent indefinitely. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;long-lived-environment-variables-in-windows&quot;&gt;Long Lived Environment Variables in Windows&lt;&#x2F;h3&gt;
&lt;iframe src=&quot;&#x2F;&#x2F;www.youtube-nocookie.com&#x2F;embed&#x2F;bEroNNzqlF4?rel=0#t=0m28s&quot; frameborder=&quot;0&quot; class=&quot;video&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;In Windows Vista, Windows 7, or WIndows 8, you can set a persistent environment variable by right clicking on &amp;quot;My Computer&amp;quot; &amp;gt; &amp;quot;Properties&amp;quot;, then clicking on &amp;quot;Advanced system settings&amp;quot; and clicking &amp;quot;Environment Variables&amp;quot; at the bottom of the dialog box. Then click &amp;quot;New...&amp;quot; and you&#x27;ll be able to name and set your variable. This variable will be available system wide. Restart Apache and you&#x27;ll be good to go!&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re following along on Windows, try setting an environment variable called MY_ENVVAR and set the value to helloWeb. Then skip down to the section on &amp;quot;Testing&amp;quot;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;persistent-environment-variables-for-apache-on-mac-and-linux&quot;&gt;Persistent Environment Variables for Apache on Mac and Linux&lt;&#x2F;h3&gt;
&lt;p&gt;There are at least two different ways to accomplish this. If you&#x27;re more experienced in this department, please chime in the comments and I&#x27;ll update this section of the post.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;1-root-acces-baby&quot;&gt;1) Root Acces, Baby!&lt;&#x2F;h4&gt;
&lt;p&gt;If you do have root access to your Mac&#x2F;Linux machine, then you should be able to edit the file &lt;strong&gt;&#x2F;etc&#x2F;apache2&#x2F;envvars&lt;&#x2F;strong&gt;. You can set an environment variable for Apache inside this file like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;export &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;MY_ENVVAR&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;helloWeb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Using Ubuntu, I had to restart the machine before MY_ENVAR was picked up by Apache. I also needed to restart apache after making a change:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sudo apachectl restart 
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h4 id=&quot;2-using-htaccess&quot;&gt;2) Using Htaccess&lt;&#x2F;h4&gt;
&lt;p&gt;If you don&#x27;t have root access, never fear! You can still set an environment variable for Apache inside an htaccess file. Create or edit a file named &lt;strong&gt;.htaccess&lt;&#x2F;strong&gt; in the directory above your public root, and add the following code to it:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;SetEnv&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; MY_ENVVAR &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;helloWeb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For this to work, you may need to have IT set Apache&#x27;s AllowOverride directive to &amp;quot;All&amp;quot; or &amp;quot;FileInfo&amp;quot; for your virtual host. Try it out first, then contact support at your hosting company if this doesn&#x27;t work (see the testing section next).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;testing-your-new-environment-variable&quot;&gt;Testing Your New Environment Variable&lt;&#x2F;h3&gt;
&lt;p&gt;No matter how you set it, we need to make sure that php picks up the environment variable you set. This is super easy. Just a make a file called envvars.php and put it the top directory of your drupal site. Put this inside your file:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;var_dump&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;getenv&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;MY_ENVVAR&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;)); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ll use the &lt;a href=&quot;http:&#x2F;&#x2F;php.net&#x2F;getenv&quot;&gt;&lt;code&gt;getenv()&lt;&#x2F;code&gt; function&lt;&#x2F;a&gt; to retrieve the environment variable. During debugging, I use &lt;code&gt;var_dump()&lt;&#x2F;code&gt; so that I can see what type of variable php is returning. Now go to your drupal site&#x27;s url and add &#x27;&#x2F;envvars.php&#x27; to the end. If you see:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(8) &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;helloWeb&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It worked! You&#x27;re ready to go on to the rest of this article. If you see:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(0) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;FALSE
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then, php didn&#x27;t pick up this variable. First try restarting Apache on that machine. If that doesn&#x27;t give you the expected result, then try contacting your hosting company&#x27;s support for assistance or leave me a comment on this post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-an-environment-variable-in-settings-php-on-drupal-6&quot;&gt;Using an Environment Variable in Settings.php on Drupal 6&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Drupal 6&lt;&#x2F;strong&gt; uses a single &lt;em&gt;connection string&lt;&#x2F;em&gt; to store the database credentials. This string contains the user name, the database name and password, the host, and port. Here&#x27;s an example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;mysql:&#x2F;&#x2F;user:password@localhost&#x2F;database
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Simply replace &#x27;user&#x27; with your database username, and so on. Then set an environment variable with this string for Drupal 6 and the &lt;a href=&quot;http:&#x2F;&#x2F;us2.php.net&#x2F;getenv&quot;&gt;php function &lt;code&gt;getenv&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to retrieve the value. Easy!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-and-retrieving-database-credentials-on-drupal-7&quot;&gt;Setting and Retrieving Database Credentials on Drupal 7&lt;&#x2F;h2&gt;
&lt;p&gt;Drupal 7 uses an array to store the database credentials. Although you could store separate parts of the credentials in separate environment variables, I prefer to use the &lt;a href=&quot;http:&#x2F;&#x2F;us2.php.net&#x2F;serialize&quot;&gt;&lt;code&gt;serialize()&lt;&#x2F;code&gt; function&lt;&#x2F;a&gt; to convert an array into a string ahead of time. I typically do this by opening an interactive php shell and copying the result:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; php&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -a
Interactive&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; shell

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt; $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;creds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; = array();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt; $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;creds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;driver&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; = &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;mysql&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt; $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;creds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;database&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; = &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;databasename&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt; $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;creds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; = &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;username&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt; $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;creds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; = &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt; $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;creds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;host&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; = &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;localhost&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt; $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;creds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;prefix&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; = &amp;#39;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt; print serialize($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;creds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a:5:{s:6:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;driver&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s:5:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;mysql&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s:8:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;database&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s:12:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;databasename&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s:8:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s:8:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s:4:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;host&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s:9:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;localhost&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s:6:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;prefix&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;s:0:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&amp;quot;;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You&#x27;ll want to replace all the dummy values above with your actual database credentials, and then copy and paste that last line &lt;strong&gt;exactly&lt;&#x2F;strong&gt; as it appears into an environment variable. Inside my settings.php, I use the following code to retrieve the environment variable:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Store the database credentials serialized in an environmental variable.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;db_creds &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unserialize&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;getenv&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;DRUPAL_DB_CRED&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;));
$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;databases &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;array &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
  &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;array &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
    &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;db_creds&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,
  ),
);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;potential-pitfalls&quot;&gt;Potential Pitfalls&lt;&#x2F;h2&gt;
&lt;p&gt;One downside of this approach is that when you set up Drupal on a new environment, you&#x27;ll have to remember what all the environment variables are that need to be set, and you&#x27;ll need to set them up appropriately. One way of addressing this problem is to make sure that your project is structured so that the directory above your webroot (public_html, html, www or whatever) is also in source control. Then make a text file documenting these environmental variables above the public root. I would probably call this file INSTALL.TXT since that&#x27;s a fairly accurate description of the information you want to convey others and future you. If you&#x27;re using github, create a wiki page to document this information.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;alternative-means-to-the-same-end&quot;&gt;Alternative Means to the Same End&lt;&#x2F;h2&gt;
&lt;p&gt;An alternative approach that sometimes works well in practice is to create a single file that contains all the different variables that change based on environment, and then add in detection logic inside that file to set variables appropriately. &lt;&#x2F;p&gt;
&lt;p&gt;While this is a really transparent, centralized way to store all this information for a small number of environments, it quickly gets out of hand for any large number of environments or contexts that you want to account for. Theoretically, this also adds some overhead to each page request that Drupal handles as well (probably even Drupal native cache hits), although this is probably neglible in practice. &lt;&#x2F;p&gt;
&lt;p&gt;You shouldn&#x27;t be mixing code and configuration any more than you have to. Configuration (like database credentials) should be changeable without forcing your code to be rewritten. By separating the code and configuration as I&#x27;ve done above, you have an elegant scalable solution that can make Drupal work for an infinite number of environments in a more flexible way.&lt;&#x2F;p&gt;
&lt;p&gt;##Just the Beginning&lt;&#x2F;p&gt;
&lt;p&gt;Anything that differs by environment can and arguably should be set as an environmental variable. If there&#x27;s anything else you want to be different in your development environment than your production or staging environment (such as API credentials), consider creating a new environmental variable. &lt;&#x2F;p&gt;
&lt;p&gt;Drop me a line in your comments to tell me how using environment variables is working for you and what you&#x27;re using environment variables on your site!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Site Review - BountyHunterWine.com</title>
		<published>2013-10-20T00:00:00+00:00</published>
		<updated>2013-10-20T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/site-review-bountyhunterwine-com/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/site-review-bountyhunterwine-com/</id>
		<content type="html">&lt;p&gt;BountyHunterWine.com offers a wide selection of wine for purchase online, but their user experience leaves something to be desired. Rather than bailing on their site, I decided to articulate the problems that I encountered and try to suggest constructive alternatives to the current implementations. This post is simply the contents of that letter.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Dear Bounty Hunter Wine,&lt;&#x2F;p&gt;
&lt;p&gt;I like the look of your elegant website and the prices are also good on your products. My purpose for using your website today is to purchase some wine for three business partners as a thank you gift. I want each business partner to receive one bottle of wine each with a customized &amp;quot;thank you&amp;quot; message. Each partner lives at a separate address. I was able to easily find the wine I wanted at a very reasonable price without any problems. I really like the &amp;quot;Price Range&amp;quot; filter.&lt;&#x2F;p&gt;
&lt;p&gt;However, I&#x27;ve run into several problems trying to place orders on your site that I wanted to make you aware of.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-ssl-certificates&quot;&gt;1) SSL Certificates&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;files.bryceadamfisher.com&#x2F;blog&#x2F;2013&#x2F;site-review-bountyhunterwine&#x2F;https-woes.png&quot; alt=&quot;https woes for bountyhunterwines.com&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Your SSL certificates are only valid for www.bountyhunterwine.com which is fine. However, I was not redirected from bountyhunterwine.com to www.bountyhunterwine.com when I went to your site. This caused the &amp;quot;invalid certificate&amp;quot; logo from thawte to appear as well as warning messages that made your site look insecure. I recommend you pay your developers to setup such a redirection to create a trustworthy environment for your customers.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-adding-products-to-the-shopping-cart&quot;&gt;2) Adding Products to the Shopping Cart&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;files.bryceadamfisher.com&#x2F;blog&#x2F;2013&#x2F;site-review-bountyhunterwine&#x2F;quantitative-errors.png&quot; alt=&quot;difficulty filling up the shopping cart&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When I got to this screen I ran into trouble. I was only allowed to add 2 recipients at a time. Adding a third recipient caused the &amp;quot;Remaining Quantity&amp;quot; to read &amp;quot;-1&amp;quot; and I was unable to click &amp;quot;Continue Shopping&amp;quot; or &amp;quot;Viewcart&amp;quot; without triggering an error message.&lt;&#x2F;p&gt;
&lt;p&gt;However, by starting over, I was able to add one wine bottle for 2 of my business partners, then I clicked &amp;quot;Continue Shopping.&amp;quot; I returned to the product, and I was able to add a bottle to my shopping cart for my third business partner.&lt;&#x2F;p&gt;
&lt;p&gt;It seems to me that there are more than 2 bottles of &amp;quot;2009 Stag&#x27;s Leap Wine Cellar&#x27;s Merlot&amp;quot; in your inventory, and that I ended doing a lot of extra work to place this order for all three of my partners.&lt;&#x2F;p&gt;
&lt;p&gt;There are several ways of improving this part of the shopping experience. I think the best experience for me would have been to indicate the total stock of wine with &amp;quot;Remaining Quantity&amp;quot; and then to allow me to add recipients and quantities of wine ONLY if the actual remaining quantity was &amp;gt;= 0. Using a Javascript alert box if the remaining quantity actually reaches 0 would tell users that there&#x27;s no more wine to be had. However, I don&#x27;t understand the benefit of preventing users from ordering as much wine as we want to while additional stocks of wine are on hand.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-account-creation-form&quot;&gt;3) Account Creation Form&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;a-privacy&quot;&gt;a. Privacy&lt;&#x2F;h4&gt;
&lt;p&gt;Once I add managed to add all the wine to the shopping cart, I started the process of creating an account. I would prefer not provide my phone number during this process. I seriously considered finding another wine store that didn&#x27;t require a phone number.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;files.bryceadamfisher.com&#x2F;blog&#x2F;2013&#x2F;site-review-bountyhunterwine&#x2F;billing-information.png&quot; alt=&quot;lots of problems on the billing forms&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;b-address-fields&quot;&gt;b. Address Fields&lt;&#x2F;h4&gt;
&lt;p&gt;I was very confused by the bottom half of this form. In particular, I would expect the address fields to match the order I write them as an address on an envelope, namely:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;Street 1
Street 2
City
State
Zip
Country
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s amazing how difficult I found it to fill in these fields when they were in a surprising order.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;c-zip-code-city-fields&quot;&gt;c. Zip Code &amp;amp; City Fields&lt;&#x2F;h4&gt;
&lt;p&gt;Since I was confused by the order of the address fields, I entered the city first then the zip code. Doing so caused some JavaScript on the page to erase the value I provided for city. Also, the city field transformed from a text field into a dropdown list. However, I didn&#x27;t notice this happen.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;d-premature-account-creation&quot;&gt;d. Premature Account Creation&lt;&#x2F;h4&gt;
&lt;p&gt;After I pressed submit, the page informed me that I failed to fill in some fields on the form (namely the city field). I had to carefully examine each field to determine where the form needed to be filled in.&lt;&#x2F;p&gt;
&lt;p&gt;Once I fixed the city field, I tried to submit the form again. This time I received the error message that a user on the site already had that email address.&lt;&#x2F;p&gt;
&lt;p&gt;What happened was that the server side code had started processing the information from my first form submission without fully validating it. This is a serious security risk and a poor user experience. The server side code should instead be checking all form fields are:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;filled in (if they are required)&lt;&#x2F;li&gt;
&lt;li&gt;validated so that make sense (ex: zip codes should be 5 digit numbers)&lt;&#x2F;li&gt;
&lt;li&gt;sanitized to prevent attacks from hackers and evil internet robots (better known as &amp;quot;bots&amp;quot;)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Only once everything is fully validated should the data provided by the user be added to the database or used by the shopping cart.&lt;&#x2F;p&gt;
&lt;p&gt;I imagine other customers have this same problem that I experienced, and I believe that this is the most crucial and (and complicated) problem to address on your site of those that I discovered.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-adding-shipping-addresses&quot;&gt;4) Adding Shipping Addresses&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;files.bryceadamfisher.com&#x2F;blog&#x2F;2013&#x2F;site-review-bountyhunterwine&#x2F;unnecessary-information.png&quot; alt=&quot;unnecessary disclosures&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;My reason for shopping at bountyhunterwine.com is to purchase wine for my three business partners. I work very closely with only one of them, and unfortunately I don&#x27;t have all of their phone numbers. I&#x27;m not sure if your shipping company requires a phone number for each destination address or not, but making the phone number optional would make it much easier for me to use your website.&lt;&#x2F;p&gt;
&lt;p&gt;Also, I&#x27;d prefer not o divulge the personal information of my business associates without their explicit consent, and I was hoping to surprise them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;CONCLUSION&lt;&#x2F;h2&gt;
&lt;p&gt;I hope that you find this feedback constructive. Let me know if you&#x27;re able to make progress on these user interface and backend suggestions I&#x27;ve provided. Best of luck with your online business venture.&lt;&#x2F;p&gt;
&lt;p&gt;Cheers
Bryce Fisher-Fleig&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Ditching Views For SQL, Part 3</title>
		<published>2013-10-13T00:00:00+00:00</published>
		<updated>2013-10-13T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/ditching-views-for-sql-part-3/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/ditching-views-for-sql-part-3/</id>
		<content type="html">&lt;p&gt;In part one of this series, I discussed &lt;a href=&quot;&#x2F;blog&#x2F;ditching-views-for-sql&quot;&gt;the reasons not to use Views&lt;&#x2F;a&gt; including the difficulty in debugging Views and the fact that Views is only in Drupal (whereas SQL is everywhere online). In part two, I walk you through some &lt;a href=&quot;&#x2F;blog&#x2F;ditching-views-for-sql-part-2&quot;&gt;simple SQL queries and tools&lt;&#x2F;a&gt; that you&#x27;d use to find the data for a sample project. In this post, I&#x27;ll connect the results of that SQL query to the Drupal theme layer so that we can actually use that dynamic content in our Drupal site without relying on Views. For today&#x27;s lesson, you should at least be comfortable with some basic Drupal theming already.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-end-goal&quot;&gt;The End Goal&lt;&#x2F;h2&gt;
&lt;p&gt;As a quick reminder from last week, this blog post is a record of how I&#x27;m planning to replace the view on my homepage with SQL and Drupal theme hooks. So, the end result we want to have is a list of the most recently created blog titles, the body field, and the node id (so we can make links out of the title). We&#x27;ll be taking the SQL we wrote last time and doing the preprocessing and theming today.&lt;&#x2F;p&gt;
&lt;p&gt;The final markup should look something like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ol &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;blog-articles&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&amp;gt;
  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article nid-1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;bryceadamfisher.com&#x2F;blog&#x2F;first-post&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&amp;gt;
      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;h3 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article-title&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&amp;gt;First Post&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;p &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article-summary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&amp;gt;Shortened summary of the post&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&amp;lt;!-- More posts go here just like the above post --&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ol&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;hook-theme-101&quot;&gt;Hook_theme() 101&lt;&#x2F;h2&gt;
&lt;p&gt;After playing around with the Drupal theme layer for a while, I&#x27;ve really fallen in love with the Drupal theme hook. This hook is really powerfully, and I&#x27;m only going to show you my favorite way to use it, but you should know that this hook gives you all kinds of flexibility in case you would like to use it a slightly different way. After reading this tutorial, check out the &lt;a href=&quot;https:&#x2F;&#x2F;api.drupal.org&#x2F;api&#x2F;drupal&#x2F;modules!system!system.api.php&#x2F;function&#x2F;hook_theme&#x2F;7&quot;&gt;API docs for hook_theme()&lt;&#x2F;a&gt; if I pique your curiosity.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;some-background-on-drupal-hooks&quot;&gt;Some Background on Drupal Hooks&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s assume you&#x27;re using a custom theme called &lt;code&gt;myblog&lt;&#x2F;code&gt;. In Drupal speak, &lt;code&gt;myblog&lt;&#x2F;code&gt; is the &amp;quot;machine name&amp;quot; of your theme. In practice, &lt;code&gt;myblog&lt;&#x2F;code&gt; acts as a PHP namespace of sorts. We use this naming convention because PHP itself causes all functions to live in the same global namespace. So, Drupal has adopted a naming convention of starting all functions declared inside a theme or module with the name of the theme (myblog) to avoid namespace collisions. In addition, all the files associated with that theme will live in a folder called &lt;code&gt;myblog&lt;&#x2F;code&gt;. &lt;&#x2F;p&gt;
&lt;p&gt;Some function names are special in Drupal. Functions that use certain names are invoked by Drupal in response to specific conditions. These specially named functions are called &amp;quot;hooks.&amp;quot; In the Drupal API docs, we talk about these functions as &lt;code&gt;hook_theme&lt;&#x2F;code&gt; (for instance), or &lt;code&gt;hook_install&lt;&#x2F;code&gt;, &lt;code&gt;hook_enable&lt;&#x2F;code&gt;, &lt;code&gt;hook_update&lt;&#x2F;code&gt;, etc., etc. Whenever you see something referenced as hook_whatever, substitute the theme (or module) namespace for the word hook. Thus, &lt;code&gt;hook_theme&lt;&#x2F;code&gt; becomes &lt;code&gt;myblog_theme&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;A quick note on Drupal 8:&lt;&#x2F;strong&gt; Drupal 8 is a rewrite of Drupal based on the awesome Symfony project. Drupal 8 uses object oriented techniques to replace the hook system found in all earlier versions of Drupal. At time of writing this, Drupal 8 is scheduled to come out sometime late 2014. My hope is that using object oriented techniques will make debugging Drupal easier by providing easier to follow &amp;quot;breadcrumbs&amp;quot; about what order code is executed in.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;getting-our-hands-dirty-with-hook-theme&quot;&gt;Getting Our Hands Dirty with Hook_theme()&lt;&#x2F;h3&gt;
&lt;p&gt;Inside your theme folder (&#x2F;sites&#x2F;all&#x2F;themes&#x2F;myblog), create or edit the file template.php. Inside this file, create a function &lt;code&gt;myblog_theme()&lt;&#x2F;code&gt; like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;myblog_theme&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;existing&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
    &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;front_blog_teasers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(
      &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;template&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39; =&amp;gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;front-blog-teaser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;
    ),
  );
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here&#x27;s the important points to note:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;This function &lt;em&gt;must&lt;&#x2F;em&gt; return a specially formatted array.&lt;&#x2F;li&gt;
&lt;li&gt;The array keys (&#x27;front_blog_teaser&#x27;) can be anything we want, as long it hasn&#x27;t already been used.&lt;&#x2F;li&gt;
&lt;li&gt;The value for &lt;code&gt;template&lt;&#x2F;code&gt; defines which template file will be parsed, but the file extension is automatically set to &amp;quot;.tpl.php&amp;quot;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This function is only invoked during Drupal cron runs, and the results are cached. So, you &lt;strong&gt;must&lt;&#x2F;strong&gt; run cron each time you make changes to this hook.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-did-we-just-do&quot;&gt;What Did We Just Do?&lt;&#x2F;h3&gt;
&lt;p&gt;We&#x27;ve done three important things with this hook:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Registered new custom theming&lt;&#x2F;li&gt;
&lt;li&gt;Connected that custom theming to a specific template in a debug-friendly way&lt;&#x2F;li&gt;
&lt;li&gt;Made new preprocess functions available&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;We&#x27;ll take advantage of each of these pieces to do everything that Views was doing for us earlier.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-new-custom-theming&quot;&gt;1. New Custom Theming&lt;&#x2F;h2&gt;
&lt;p&gt;Anywhere we want to display our dynamic content, we can invoke:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;theme&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;front_blog_teaser&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you&#x27;re accustomed to working with Views, the equivalent code would be something like:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;views_embed_view&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;name_of_view&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this project for my blog, I&#x27;d place this code just under the &lt;code&gt;$content&lt;&#x2F;code&gt; region in page--front.tpl.php. &lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-connected-that-custom-theming-to-a-specific-template-in-a-debug-friendly-way&quot;&gt;2. Connected that custom theming to a specific template in a debug-friendly way&lt;&#x2F;h2&gt;
&lt;p&gt;In my opinion, one of the worst parts of Views is the God-awful template names that it forces you to use. I&#x27;m sorry, but views-view-unformatted--blog-article-block-3.tpl.php is a mystery to me. Especially as a site grows in size and complexity, you end with dozens of views templates. The file name for the template does not provide any insight into where you might expect that template to be used in the site.&lt;&#x2F;p&gt;
&lt;p&gt;Views 2 for Drupal 6 tried to help by helping you track the templates being used inside in the Views Admin UI. However if the view templates are nested at all, even the Views Admin can&#x27;t explain it to you. Kiss your afternoon goodbye trying to untangle the web of views templates.&lt;&#x2F;p&gt;
&lt;p&gt;Using &lt;code&gt;hook_theme&lt;&#x2F;code&gt;, we can do a text search for the name of the template across all files in the project. Such a search would show us where this template is registered, and we&#x27;d know to look for somewhere &lt;code&gt;print theme(&#x27;front_blog_teaser&#x27;);&lt;&#x2F;code&gt; was written to see how this code ended up in the browse. There&#x27;s no equivalent searchable, debuggable way to uncover what&#x27;s going on in Views.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-to-put-inside-the-template&quot;&gt;What to put inside the template&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s actually write the template file code now. Inside the directory &#x2F;sites&#x2F;all&#x2F;themes&#x2F;myblog&#x2F;, create the file &lt;code&gt;front-blog-teaser.tpl.php&lt;&#x2F;code&gt;. Remember, we&#x27;ll want to display any number of different post teasers on the homepage. Let&#x27;s take the markup we want to end up with (above), and substitute imaginary variable names into that html. Later, we&#x27;ll make sure these variables get injected just how we want them, but for now let&#x27;s focus on the end result. Here&#x27;s what I would do:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ol &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;blog-articles&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&amp;gt;
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;foreach&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;articles &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;as $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article nid-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;node&#x2F;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;); &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&amp;gt;
      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;h3 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article-title&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;p &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article-summary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;print &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;summary&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;endforeach&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ol&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Quick observations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;An array of objects named &lt;code&gt;articles&lt;&#x2F;code&gt; must be injected into this template&lt;&#x2F;li&gt;
&lt;li&gt;Each object inside &lt;code&gt;articles&lt;&#x2F;code&gt; should have the properties, title, nid, and summary.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;a-word-on-debugging-templates&quot;&gt;A Word On Debugging Templates&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;ve chosen each and every variable name because it makes sense to me. However, you could change any of these variables. When debugging the equivalent code in a view template, you would have to use the variables that views forced you to use. Normally that might actually be a good thing, because then no matte who is maintaining this code, Views will always use the same variable names in the same situation. &lt;&#x2F;p&gt;
&lt;p&gt;I often want to see what variables are available using &lt;code&gt;var_dump()&lt;&#x2F;code&gt; to print the values and data types of an expression. With Views templates, the variables injected into the template have so much recursion that &lt;code&gt;var_dump&lt;&#x2F;code&gt; will cause PHP to run out of memory and crash. This effectively makes me blind and unable to see what variables are available in the template. Simple changes can take days and have unintended consequences because I don&#x27;t have good insight into the data that&#x27;s available. &lt;&#x2F;p&gt;
&lt;p&gt;By using preprocess functions, the data is dead simple. You can inject primitive data types and know exactly what to expect. If you want to change something, there&#x27;s a trail in &lt;code&gt;hook_theme&lt;&#x2F;code&gt; connecting the template, the custom theming name (ex: &#x27;front_blog_teasers&#x27;) and the template altogether. The time I spend programming, the more I come to rely on these sorts of breadcrumbs to solve the daily grind of problems that fall into my lap.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-made-preprocess-functions-available-for-this-custom-theming&quot;&gt;3. Made preprocess functions available for this custom theming&lt;&#x2F;h2&gt;
&lt;p&gt;This is the only piece of black magic you need to use. I saw black magic because there&#x27;s no strong trail of breadcrumbs connecting this function to &lt;code&gt;hook_theme&lt;&#x2F;code&gt;. You just have to know about this trick. (Although, if you read carefully through the API docs for hook_theme, you&#x27;ll see how to explicitly spell out a preprocess function.) &lt;&#x2F;p&gt;
&lt;p&gt;By default, the name of the preprocess function is &lt;code&gt;themename_process_HOOK&lt;&#x2F;code&gt;. So, for our example, the theme name is &lt;code&gt;myblog&lt;&#x2F;code&gt; and the HOOK is &lt;code&gt;front_blog_teasers&lt;&#x2F;code&gt;. So, the preprocess function must be named &lt;code&gt;myblog_preprocess_front_blog_teasers&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-s-a-preprocess-function&quot;&gt;What&#x27;s a Preprocess Function?&lt;&#x2F;h3&gt;
&lt;p&gt;In Drupal, registering a new custom theming in hook_theme automatically makes a &amp;quot;preprocess&amp;quot; function available. This is where you get to do any special logic to gather and prepare any variables needed in the template. You also get to name the variables and inject them into the template using references.&lt;&#x2F;p&gt;
&lt;p&gt;So for our situation, we&#x27;ll want to put the drupal SQL query we wrote in part 2 inside the preprocess function and then pass those variables back to the template. Here&#x27;s how we do that:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;myblog_preprocess_front_blog_teasers&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;vars&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;) {
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Query all the raw blog teaser data
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;db_query&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;quot;
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;nid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;body_value
      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; {node} n
      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;JOIN&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; {field_data_body} b
        ON &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;nid &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;entity_id
     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WHERE &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\&amp;quot;
       &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;AND &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;status &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ORDER BY &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;created &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;DESC
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;);
  
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Format the raw data as PHP variables
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;articles &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;db_fetch_object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)) {
    $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;summary &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;mb_substr&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;body_value&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);
    $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;articles&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[] = $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
  }

  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Inject these variables into the template
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;vars&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;articles&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;] = $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;articles&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;?&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s only two changes here. You notice that we queried the database for the whole body value using SQL, but we&#x27;re displaying the article summary in the template. I added this line of code to create the summary: &lt;code&gt;$row-&amp;gt;summary = mb_substr($row-&amp;gt;body_value, 0, 200);&lt;&#x2F;code&gt;. The reason I did this was to demonstrate how you might prepare some special information for the template. I should point out however that there is a whole field in the database for the summary and you should probably you use that field instead since blog authors might use a different summary than the first 200 characters of their article.&lt;&#x2F;p&gt;
&lt;p&gt;The other thing worth noting is how we injected the data into the templates. We simply use create an array key in the $vars variable. Since we used &lt;code&gt;&amp;amp;$vars&lt;&#x2F;code&gt; as the parameter to this method, any changes to the &lt;code&gt;$vars&lt;&#x2F;code&gt; variable will automatically be available as if we returned those changes. I don&#x27;t love that feature of Drupal, but PHP only allows you to return one value for a function, so I understand the reasoning for this confusing convention. Note that every key in $vars array will a variable in the template.&lt;&#x2F;p&gt;
&lt;p&gt;Save the files, clear the freaking Drupal cache, and you should have your html appear just as you wanted it to. I&#x27;ll leave it to your imagination to setup the proper CSS styling for this project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;taking-it-farther&quot;&gt;Taking It Farther&lt;&#x2F;h2&gt;
&lt;p&gt;We could have done a lot more. We could used SQL to capture the number of comments associated with an article, the date, the author&#x27;s name, and the image field for each post. Using the tools and techniques in parts 2 and 3, you easily accomplish this on your own. You could also leverage the Drupal caching system to store the final template markup in your preprocess function. The best way to take this farther is to experiment with the other possibilities using hook_theme. We&#x27;ve just barely scratched the surface!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Although it takes more time to setup, using SQL and &lt;code&gt;hook_theme()&lt;&#x2F;code&gt; allowed us to write easily debuggable and understandable code with much less CPU overhead than running Views. You&#x27;ll thank yourself at 1am when you need to fix something fast on the homepage that you didn&#x27;t use Views. &lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Ditching Views For SQL, Part 2</title>
		<published>2013-10-09T00:00:00+00:00</published>
		<updated>2013-10-09T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/ditching-views-for-sql-part-2/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/ditching-views-for-sql-part-2/</id>
		<content type="html">&lt;p&gt;I&#x27;m going to dive into some actual code in this post to demostrate Drupal without Views. I&#x27;m only going to cover how to write the SQL — I&#x27;ll cover the theming functions in a third part. &lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m starting from the assumption that you&#x27;re already comfortable with SQL (particularly select statement and joins). If you don&#x27;t understand what I just wrote, look up tutorials on those topics before trying to read here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;database-abstraction-layer-in-drupal&quot;&gt;Database Abstraction Layer in Drupal&lt;&#x2F;h2&gt;
&lt;p&gt;The typical pattern for getting data out of the database using SQL is:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Examine the database structure&lt;&#x2F;li&gt;
&lt;li&gt;Write a working SQL query&lt;&#x2F;li&gt;
&lt;li&gt;&amp;quot;Drupalize&amp;quot; the sql syntax&lt;&#x2F;li&gt;
&lt;li&gt;Pass the query through &lt;code&gt;db_query()&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Retrieve query results using &lt;code&gt;while&lt;&#x2F;code&gt; and &lt;code&gt;db_fetch_object()&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For this article, we&#x27;ll write a query that grabs the node ids, titles, and bodies for the last three blog posts. Let&#x27;s imagine I&#x27;m rewriting my homepage view. So we&#x27;ll want the blog posts in date order, starting with the most recent at the top of the list. At the end of this post, our code should return an array of three each objects with the properties &lt;code&gt;nid&lt;&#x2F;code&gt;, &lt;code&gt;title&lt;&#x2F;code&gt;, and &lt;code&gt;body&lt;&#x2F;code&gt; ordered by created date.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-examine-the-database-structure&quot;&gt;1. Examine the Database Structure&lt;&#x2F;h3&gt;
&lt;p&gt;There&#x27;s a lot of tables in Drupal. Usually finding the fields we want involving poking around in the database. &lt;strong&gt;Never poke around the live database&lt;&#x2F;strong&gt; since you could ruin your site. Instead, download a copy of the database to your own computer and look at the database in your favorite MySQL client. If don&#x27;t have one already, I highly recommend HeidiSQL for windows. You could PhpMyAdmin, but I find it slow and complicated compared to a desktop application like HeidiSQL.&lt;&#x2F;p&gt;
&lt;p&gt;After poking around in my database, I found that the &lt;code&gt;node&lt;&#x2F;code&gt; table contained almost everything I needed: the content_type, title, nid (node id), and created. The only thing missing was the body field.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-write-a-working-sql-query&quot;&gt;2. Write a Working SQL Query&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s ignore the body field for a moment just get most of our query written:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; nid, title
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;  node
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; type = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
     AND status = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ORDER BY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; created &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;DESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you run that query in the &amp;quot;query&amp;quot; tab on HeidiSQL, you should see three results in the correct order with the nid and title fields. The only thing to explain here is that &lt;code&gt;status = 1&lt;&#x2F;code&gt; clause filters out unpublished nodes. So far so good! Okay, let&#x27;s add in the body field now.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;body-field-drupal-6&quot;&gt;Body Field Drupal 6&lt;&#x2F;h4&gt;
&lt;p&gt;If you&#x27;re on Drupal 6, the body field is always stored in the &lt;code&gt;node_revision&lt;&#x2F;code&gt; table. Just join the &lt;code&gt;node&lt;&#x2F;code&gt; and &lt;code&gt;node_revision&lt;&#x2F;code&gt; tables on the vid field. Here&#x27;s the SQL for Drupal 6:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; nid, title, body
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; node
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;JOIN&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; node_revision
      ON &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;vid &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;node_revision&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;vid
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; type = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
     AND status = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ORDER BY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; created &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;DESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h4 id=&quot;body-field-drupal-7&quot;&gt;Body Field Drupal 7&lt;&#x2F;h4&gt;
&lt;p&gt;Drupal 7 is a little different. The body field is in the &lt;code&gt;field_data_body&lt;&#x2F;code&gt; table, and the field is called body_value. Here&#x27;s the SQL Drupal 7:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; nid, title, body_value
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; node
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;JOIN&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; field_data_body
      ON &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;nid &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;field_data_body&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;entity_id
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; type = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
     AND status = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ORDER BY&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; created &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;DESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;3-drupalize-the-sql-syntax&quot;&gt;3. Drupalize the SQL Syntax&lt;&#x2F;h3&gt;
&lt;p&gt;Drupal&#x27;s database abstraction layer allows you to automatically prefix your table names in the queries. (It also allows parameterized queries which is vital to security, but it doesn&#x27;t apply to our situation). Doing this step makes your SQL more portable and is really easy. All we need to do is wrap curly braces around the table names. By convention, queries also use aliases for table names. Here&#x27;s the SQL for Drupal 6:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;nid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;r&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;body
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; {node} n
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;JOIN&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; {node_revision} r
      ON &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;vid &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;r&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;vid
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WHERE &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
     AND &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;status &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ORDER BY &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;created &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;DESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here&#x27;s the SQL for Drupal 7:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;nid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;body_value
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; {node} n
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;JOIN&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; {field_data_body} b
      ON &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;nid &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;entity_id
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WHERE &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;
     AND &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;status &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ORDER BY &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;created &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;DESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;4-pass-the-query-through-db-query&quot;&gt;4. Pass the query through &lt;code&gt;db_query()&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;For brevity&#x27;s sake I&#x27;ll show you the Drupal 7 version only:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;query &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;SELECT &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;nid&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;body_value
            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; {node} n
            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;JOIN&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; {field_data_body} b
              ON &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;nid &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;b&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;entity_id
           &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;WHERE &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;type &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\&amp;quot;
             &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;AND &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;status &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ORDER BY &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;created &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;DESC&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;
$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;db_query&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s important to keep the return value of &lt;code&gt;db_query()&lt;&#x2F;code&gt; so that we can actually pull out the individual rows from the database. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-retrieve-query-results-using-while-and-db-fetch-object&quot;&gt;5. Retrieve Query Results Using &lt;code&gt;while&lt;&#x2F;code&gt; and &lt;code&gt;db_fetch_object()&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Here&#x27;s how this bit works. &lt;code&gt;db_fetch_object&lt;&#x2F;code&gt; is given the $result of a previously run query. If there is another row among the results, &lt;code&gt;db_fetch_object&lt;&#x2F;code&gt; will return an object with each field of the SQL result as a property on the object. In our example, the object will have three properties: title, nid, and body. &lt;&#x2F;p&gt;
&lt;p&gt;However, if there&#x27;s no more results from the query, then &lt;code&gt;db_fetch_object()&lt;&#x2F;code&gt; returns false. In PHP, if you assign a &amp;quot;falsy&amp;quot; value to a variable, that whole expression evaluates to false. This is really handy in while loops. Again, I&#x27;m only showing the Drupal 7 version for brevity (&lt;code&gt;db_query()&lt;&#x2F;code&gt; and &lt;code&gt;db_fetch_object()&lt;&#x2F;code&gt; are pretty much the same in those versions of drupal). This code follows the code above:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Store the results here.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nodes &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;array&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;();
    
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Continue pulling out result rows from the query results,
&#x2F;&#x2F; but stop if there are no more.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;row &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;db_fetch_object&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;)) {
    $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nodes&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[] = $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;row&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
}
   
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;Output the result to the browser
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;var_dump&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;($&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;nodes&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Again, the only thing to really explain here is that when db_fetch_object reaches that last row in the result set, it will return false. Setting $row to false makes everything inside the while conditional evaluate to false. That kills the while loop. Simple and elegant!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;In this article, we&#x27;ve walked through some tools to help discover the layouts of tables in Drupal (don&#x27;t be afraid to explore locally!), written a working SQL query in HeidiSQL, then ported this query to PHP&#x2F;Drupal code. Ready to implement these features in the theme layer? Keep reading &lt;a href=&quot;http:&#x2F;&#x2F;bryceadamfisher.com&#x2F;blog&#x2F;ditching-views-for-sql-part-3&quot;&gt;part 3 in the Ditch Views for SQL saga&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Ditching Views For SQL</title>
		<published>2013-10-07T00:00:00+00:00</published>
		<updated>2013-10-07T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/ditching-views-for-sql/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/ditching-views-for-sql/</id>
		<content type="html">&lt;p&gt;I&#x27;ve been using Views in Drupal since I first started using Drupal. It&#x27;s easy to create dynamic content that stays up to date throughout the site, and on, and on, and on. It&#x27;s the most comprehensive SQL query builder that I&#x27;ve used, and it&#x27;s a truly magical piece of software. The Drupal community owns a lot to Merlin of Chaos and the legion of Views contributors.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-wrong-with-views&quot;&gt;What&#x27;s Wrong with Views?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Debugging.&lt;&#x2F;strong&gt; Like many parts of the current Drupal 7 (and Drupal 6) ecosystem, Views works like a charm until something goes wrong. At my 9 to 5 job today, I ran into a situation where I needed to change my underlying content type and now my template started displaying random fields. No problem? Views creates a whole host of wonderful preprocessed and postprocessed variables available in the template. So, like any good themer, I &lt;code&gt;var_dump()&lt;&#x2F;code&gt;&#x27;d the $node object in search of the data I wanted. A quick search through the output showed me that the string was available somewhere inside the $node object. Hooray! Problem solved.&lt;&#x2F;p&gt;
&lt;p&gt;Sadly, &lt;strong&gt;this was on line 8907, and I couldn&#x27;t see how to drill down&lt;&#x2F;strong&gt; to this variable from the $node object. I decided to write a recursive function to search through all properties of the $node object, and when I left work two hours later, I still hadn&#x27;t gotten this recursive function quite right. Certainly, I could stick with Views and find a way to output the raw field value.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-heart-of-the-problem&quot;&gt;The Heart of the Problem&lt;&#x2F;h2&gt;
&lt;p&gt;However, let&#x27;s take a step back and think through what I&#x27;m doing here. Views is supposed to prevent me from having to deal with the complexity of writing SQL queries and formatting the outputting. Click, click, click, and you&#x27;re done. The problem is that while I actually understand SQL fairly well, I don&#x27;t have any idea what&#x27;s going in Views when things break. And, due to the unusual complexity of the Views module, it&#x27;s many plugins and API and recursive objects, it would take me days to troubleshoot even a relatively simple problem like this. Don&#x27;t get me wrong, I &lt;em&gt;like&lt;&#x2F;em&gt; learning new things, but this is a not a good use of my employer&#x27;s time.&lt;&#x2F;p&gt;
&lt;p&gt;The heart of the problem is that Views, while catering to those who are unable to write SQL, is creating overwhelming complexity for everyone. Part of the reason for this is that Views tries to be comprehensive. It features a whole new theme layer, new url routing logic, extra access control, a suite of plugins to format raw field values, live previews, import&#x2F;export from code, and a whole bunch of other goodies. In a word, &lt;strong&gt;Views is trying to do too much&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-right-way-to-approach-sql&quot;&gt;The Right Way to Approach SQL&lt;&#x2F;h2&gt;
&lt;p&gt;With my degree in philosophy, I tend to be shy about using the &amp;quot;R&amp;quot; word, but I think we can safely say the &amp;quot;normal&amp;quot; and sane way to ease the burden of writing SQL is to create Object Relational Mapper (ORM). An ORM is an internal API for accessing the database. In CakePHP, &lt;a href=&quot;http:&#x2F;&#x2F;book.cakephp.org&#x2F;2.0&#x2F;en&#x2F;tutorials-and-examples&#x2F;blog&#x2F;part-two.html#create-a-posts-controller&quot;&gt;the ORM is the first thing you learn&lt;&#x2F;a&gt;, about CakePHP after doing  the installation. Similarly, it&#x27;s impossible to do much in Code Igniter or Ruby on Rails without leveraging an ORM. Drupal 7 has taken some steps in this direction with the &lt;a href=&quot;https:&#x2F;&#x2F;api.drupal.org&#x2F;api&#x2F;drupal&#x2F;includes!entity.inc&#x2F;class&#x2F;EntityFieldQuery&#x2F;7&quot;&gt;EntityFieldQuery&lt;&#x2F;a&gt; class, but it&#x27;s probably the last thing you learn about in Drupal. Also, it tends to be a little bit confusing.&lt;&#x2F;p&gt;
&lt;p&gt;For my day job, I&#x27;m currently stuck with Drupal 6, so I don&#x27;t even have the EntityFieldQuery class available. Rather than languish in despair, I actually find the SQL quite easy to write. In all honesty, rewriting the code that tripped me up today in SQL is probably faster than figuring what went wrong in my views template. If you don&#x27;t know SQL yet, consider taking the time to learn it. Unlike Views, you can use your experience with SQL outside the Drupal community and even with other programming languages like Python&#x2F;Django, or Ruby&#x2F;Rails. I think you&#x27;ll find that this is a good investment in your career as a programmer. &lt;&#x2F;p&gt;
&lt;h2 id=&quot;up-next&quot;&gt;Up Next&lt;&#x2F;h2&gt;
&lt;p&gt;Ready to make the shift? Check out &lt;a href=&quot;&#x2F;ditching-views-for-sql-part-2&#x2F;&quot;&gt;Ditching Views part 2&lt;&#x2F;a&gt; to learn about how to implement my advice on your own drupal instance.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>The Missing Guide to Subnet Masks</title>
		<published>2013-10-05T00:00:00+00:00</published>
		<updated>2013-10-05T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/the-missing-guide-to-subnet-masks/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/the-missing-guide-to-subnet-masks/</id>
		<content type="html">&lt;p&gt;Recently, I tried to understand what was going on in terms of IP ranges in my firewall configuration, and I had an impossible time trying to find a succinct and clear explanation. This post aims to make it a little easier to understand how to read and specify ranges of IP addresses using subnet masks. &lt;&#x2F;p&gt;
&lt;h2 id=&quot;sexy-subnet-masks-in-binary&quot;&gt;Sexy Subnet Masks in Binary&lt;&#x2F;h2&gt;
&lt;p&gt;Subnet masks are often used by system administrators to specify ranges of IP addresses in a compact format. In this context, the word &lt;em&gt;mask&lt;&#x2F;em&gt; means a binary number used to transform an IP Address using boolean algebra (AND, OR, XOR operations). While subnet masks might not be sexy, seeing the binary form of a subnet mask is the only way to visualize what&#x27;s going on. Let&#x27;s start with an example subnet mask:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;11111111 . 11111111 . 11111111 . 11110000&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Notice that the subnet mask consists of 4 octets of bits. This mask is used to transform an IP address. So let&#x27;s take an imaginary IP address, 50.122.34.21, to play with. We&#x27;ll need to convert this address into 4 octets of bits so that we can easily visualize what&#x27;s going on:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;00110010 . 01111010 . 00100010 . 00010101 (&lt;strong&gt;IP Address&lt;&#x2F;strong&gt;)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;straight-up-boolean-baby&quot;&gt;Straight Up Boolean, Baby&lt;&#x2F;h2&gt;
&lt;p&gt;All subnet masks use boolean AND to combine the IP Address and the mask. Let&#x27;s review how boolean AND works:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;1 AND 1 = 1.&lt;br&gt;
0 AND 1 = 0.&lt;br&gt;
1 AND 0 = 0.&lt;br&gt;
0 AND 0 = 0.&lt;br&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Okay, now putting that altogether, let&#x27;s AND the IP address and the subnet mask together:&lt;&#x2F;p&gt;
&lt;div class=&quot;scroll&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;First Octect&lt;&#x2F;th&gt;
        &lt;th&gt;Second Octect&lt;&#x2F;th&gt;
        &lt;th&gt;Third Octet&lt;&#x2F;th&gt;
        &lt;th&gt;Fourth Octect&lt;&#x2F;th&gt;
        &lt;th&gt;Name&lt;&#x2F;th&gt;
      &lt;&#x2F;tr&gt;
    &lt;&#x2F;thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;00110010&lt;&#x2F;td&gt;   
        &lt;td&gt;01111010&lt;&#x2F;td&gt;   
        &lt;td&gt;00100010&lt;&#x2F;td&gt;  
        &lt;td&gt;00010101&lt;&#x2F;td&gt;
        &lt;td&gt;&lt;strong&gt;IP Address&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;
      &lt;&#x2F;tr&gt;
      &lt;tr&gt;
        &lt;td&gt;11111111&lt;&#x2F;td&gt;   
        &lt;td&gt;11111111&lt;&#x2F;td&gt;   
        &lt;td&gt;11111111&lt;&#x2F;td&gt;  
        &lt;td&gt;11110000&lt;&#x2F;td&gt;
        &lt;td&gt;&lt;strong&gt;Subnet Masks&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;
      &lt;&#x2F;tr&gt;
      &lt;tr&gt;
        &lt;td&gt;00110010&lt;&#x2F;td&gt;   
        &lt;td&gt;01111010&lt;&#x2F;td&gt;   
        &lt;td&gt;00100010&lt;&#x2F;td&gt;  
        &lt;td&gt;00010000&lt;&#x2F;td&gt;
        &lt;td&gt;&lt;strong&gt;Result&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;
      &lt;&#x2F;tr&gt;
    &lt;&#x2F;tbody&gt;
  &lt;&#x2F;table&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Notice how all the ones on the left side of the subnet mask allow the starting IP address to trickle down into the result. Only the rightmost bits (where the mask has 0&#x27;s) are altered. At the end of the process, we get 50.122.34.16 converting the binary result back into decimal.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-subnet-mask-by-any-other-name&quot;&gt;A Subnet Mask By Any Other Name&lt;&#x2F;h2&gt;
&lt;p&gt;So, why would we care about getting out 50.122.34.16? We care because we now have a way of saying &amp;quot;do something to all the IP addresses between 50.122.34.21 and 50.122.34.16&amp;quot;. Let&#x27;s take this a little farther and make subnet masks more useful. Look at the binary form of the subnet mask once more:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;11111111 . 11111111 . 11111111 . 11110000&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Notice, that all the 1&#x27;s are on the left and all the 0&#x27;s are on the right. This is no accident. Subnet masks are not allowed to put ones and zeros anywhere you like. Some number of 1&#x27;s must go on the left side, and the rest of the bits will be zero. Since this is the case, you could abbreviate the subnet mask by just counting the number of 1&#x27;s. For instance the above subnet masks could just as easily written as:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&#x2F;28&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Likewise, these two notations are also equivalent:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&#x2F;8&lt;br&gt;
11111111 . 00000000 . 00000000 . 00000000&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;So, combining this short form of the subnet mask and the original IP address could also be written like:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;50.122.34.21**&#x2F;28**&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;While this form is harder for mere mortals like myself to read, I suspect that this format is much much faster for networking devices to compute and store. It also has the advantage of being much shorter than writing out the full version of two IP addresses. In the literature on this topic, you often find the decimal notation for subnet masks as well. For instance, &#x2F;28 would be written out as 255 . 255 . 255 . 240. Personally, I find this notation unhelpful, so I&#x27;ve avoided it in this article, but you should expect to see it written this way sometimes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-gotcha&quot;&gt;The Gotcha&lt;&#x2F;h2&gt;
&lt;p&gt;You knew it was coming, didn&#x27;t you? Notice that our transformed IP address from the example above (50.122.34.16) ends in 16. This too is no accident. Since our subnet mask uses binary numbers, the transformed result will nearly always end in a power of two. This makes starting or ending an IP range on an arbitrary number between powers of two hard, because you&#x27;ll need to break down the range into a series of more binary friendly sub-ranges. I&#x27;ll leave you play around with the math yourself.&lt;&#x2F;p&gt;
&lt;p&gt;There are some great &lt;a href=&quot;http:&#x2F;&#x2F;www.subnetmask.info&#x2F;&quot;&gt;subnet mask calculators online&lt;&#x2F;a&gt; that can help play with the examples above or your own firewall configuration to better understand the concepts in this article. &lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Firewalls often use subnet masks in the compact form IP&#x2F;number of 1&#x27;s to specify a range of IP addresses. Understanding the boolean AND operation is the key to understanding how these subnet masks work.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>7 Steps to Improving Page Load Time on Shared Hosting</title>
		<published>2013-06-08T00:00:00+00:00</published>
		<updated>2013-06-08T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/7-steps-to-improving-page-load-time-on-shared-hosting/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/7-steps-to-improving-page-load-time-on-shared-hosting/</id>
		<content type="html">&lt;p&gt;I recently optimized page load time for the &lt;a href=&quot;http:&#x2F;&#x2F;spelling-words-well.com&quot;&gt;Spelling Word&#x27;s Well homepage&lt;&#x2F;a&gt; as part of a redesign. Using the &lt;a href=&quot;http:&#x2F;&#x2F;yslow.org&quot;&gt;YSlow Add-on&lt;&#x2F;a&gt; helped, but I still had a learning curve to face, and I also uncovered some other tricks. Here&#x27;s a summary of the most important steps I took:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-make-a-diy-cookie-free-content-delivery-network&quot;&gt;1. Make a DIY, Cookie-free Content Delivery Network&lt;&#x2F;h2&gt;
&lt;p&gt;The key ingredient here is to use a subdomain that delivers &lt;strong&gt;all&lt;&#x2F;strong&gt; your static content: images, css, static json files, xml dumps, javascript, etc. So, you&#x27;ll need to assemble all those assets if they are scattered around. I often have cPanel available on my shared hosting sites. Log into cPanel and click &lt;strong&gt;Subdomains&lt;&#x2F;strong&gt; in the Domains box. &lt;!--You&#x27;ll see something like this:--&gt;&lt;&#x2F;p&gt;
&lt;!--![cPanel form for creating a subdomain](&#x2F;sites&#x2F;default&#x2F;files&#x2F;field&#x2F;image&#x2F;cPanel-make-subdomain.png)--&gt;
&lt;p&gt;Choose a subdomain name that makes sense to you. I like to store these files outside of the public directory of the main domain (hence &#x2F;subdomains&#x2F;static instead of &#x2F;public_html&#x2F;static in the screenshot). Once this directory is created, be ruthless about putting everything you can on this subdomain, and update your html to use this subdomain.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;&#x2F;strong&gt; Finally, make your DIY CDN real &lt;a href=&quot;https:&#x2F;&#x2F;www.cloudflare.com&#x2F;features-cdn&quot;&gt;using CloudFlare for free&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE April, 2014:&lt;&#x2F;strong&gt; See my walkthrough describing how to &lt;a href=&quot;&#x2F;blog&#x2F;setting-up-ssl-on-aws-cloudfront-and-s3&#x2F;&quot;&gt;setup AWS CloudFront with a custom SSL cert and an AWS S3 bucket&lt;&#x2F;a&gt; (what I do on this site) for as little as $10 for the 1st year.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-supercharge-your-subdomain&quot;&gt;2. Supercharge Your Subdomain&lt;&#x2F;h2&gt;
&lt;p&gt;Create an htaccess file in the root directory of your subdomain (in my example, &#x2F;subdomains&#x2F;static&#x2F;.htaccess). The following .htaccess file asks browsers to store images and other static assets in it&#x27;s cache longer for better repeat traffic. It utilizes mod_deflate to compress all kinds of text files (js, xml, html, css, ajax, etc.) over the netwok. It also blocks access to directory listings and sensitive batch files for security.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;# Security
Options -Indexes
&amp;lt;IfModule mod_rewrite.c&amp;gt;
RewriteEngine On
RewriteRule \.bat$ - [R=404]
&amp;lt;&#x2F;IfModule&amp;gt;
 
# Leverage Browser Caching
&amp;lt;IfModule mod_expires.c&amp;gt;
# Enable Expirations
ExpiresActive On
 
# Default Expiration Time
ExpiresDefault &amp;quot;access plus 1 month&amp;quot;
 
# Expiration for Images
ExpiresByType image&#x2F;gif &amp;quot;access plus 1 month&amp;quot;
ExpiresByType image&#x2F;png &amp;quot;access plus 1 month&amp;quot;
ExpiresByType image&#x2F;jpg &amp;quot;access plus 1 month&amp;quot;
ExpiresByType image&#x2F;jpeg &amp;quot;access plus 1 month&amp;quot;
 
# Expiration for CSS
ExpiresByType text&#x2F;css &amp;quot;access plus 1 month&amp;quot;
 
# Expiration for JavaScript
ExpiresByType application&#x2F;javascript &amp;quot;access plus 1 month&amp;quot;
&amp;lt;&#x2F;IfModule&amp;gt;
 
# Gzip
&amp;lt;ifmodule mod_deflate.c&amp;gt;
AddOutputFilterByType DEFLATE text&#x2F;text text&#x2F;html text&#x2F;plain text&#x2F;xml text&#x2F;css application&#x2F;x-javascript application&#x2F;javascript text&#x2F;javascript
&amp;lt;&#x2F;ifmodule&amp;gt;
#End Gzip
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;3-fight-the-scourge-of-social-sharing-widgets&quot;&gt;3. Fight the Scourge of Social Sharing Widgets&lt;&#x2F;h2&gt;
&lt;p&gt;Seriously, this is the most important thing. Each facebook like and google sharing widget REALLY slows down the page. To use these widgets, the browser has to look up the IP address of the domain, contact the real server, ask for the javascript, run the javascript, lookup another domain&#x27;s IP address for the images, ask for the images, download the images, and then display them. It&#x27;s all horribly inefficient. It&#x27;s important to weight the lost conversions on your site against the possible the sharing benefits of showing the widget. Not everyone will share your page, but everyone will have to suffer through another agonizing second of page load time for every widget on every page.&lt;&#x2F;p&gt;
&lt;p&gt;So, how do you fight back?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reduce unnecessary social widgets&lt;&#x2F;strong&gt; On Spelling Words Well, I removed a facebook badge that was hiding down the page, and saved almost a second in page load time.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Load Widgets using AJAX&lt;&#x2F;strong&gt; Many widgets do this already, but check the documentation for more info.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Look for simpler alternatives&lt;&#x2F;strong&gt; You can download the images, javascript, and html and server from your own CDN and avoid a lot of the overhead of googleplus and facebook widgets without sacrificing the ability to share. Thank you Twitter!!! (Also, your designers can go to town on the sharing widgets).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;&#x2F;strong&gt; In a future post, I&#x27;ll walk you through using twitter web intents in a more detailed way, and I&#x27;d like to discuss creating customized performance focused google plus sharing widgets that function similarly to the twitter web intents. Follow &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;BryceAdamFisher&quot;&gt;me on twitter&lt;&#x2F;a&gt; to find out when I write this follow up article!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;4-optimize-images&quot;&gt;4. Optimize Images&lt;&#x2F;h2&gt;
&lt;p&gt;There&#x27;s two parts to optimizing your images. The (more) obvious part is that you should use the lowest quality settings for JPGs that you can bear to look at. This makes the filesize smaller without shrinking the image&#x27;s display size on screen. PNG files can be compressed using the free &lt;a href=&quot;http:&#x2F;&#x2F;gimp.org&#x2F;&quot;&gt;GIMP&lt;&#x2F;a&gt; photo editing wonderland software. Don&#x27;t use gifs. Just don&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE October, 2013&lt;&#x2F;strong&gt;: I now recommend the incredible (and currently free) service from &lt;a href=&quot;http:&#x2F;&#x2F;kraken.io&quot;&gt;kraken.io&lt;&#x2F;a&gt; to shrink images an exta 10-25% on average. Kraken does this without sacrificing any image quality. You can drag and drop dozens of files and download the optimized images as a single zip file once Kraken has worked it&#x27;s magic.&lt;&#x2F;p&gt;
&lt;p&gt;The somewhat less obvious part is to crop your images in your photo-editing software (or in Drupal or Wordpress) so that the browser won&#x27;t have to resize the image on fly. Setting a size in the &lt;code&gt;&amp;lt;img &#x2F;&amp;gt;&lt;&#x2F;code&gt; tag that is different the size of image stored in the server will force the browser to do extra work to resize the image. While is this really cool, it is slower. Of course, if you&#x27;re using a responsive design, you&#x27;ll have to weigh this carefully, and probably disregard my advice. Responsive images have a whole host of other puzzles to untangle that requires several posts to tease out.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;5-make-the-page-beautiful-without-javascript&quot;&gt;5. Make the Page Beautiful Without JavaScript&lt;&#x2F;h2&gt;
&lt;p&gt;First of all, Google is your biggest fan, and Google doesn&#x27;t use javascript when it&#x27;s examining your site. Also, a small percentage of your visitors will not be using JavaScript either, so you want to make sure those folks have a nice experience. The main thing to do is to place something simpler but useful where the JavaScript content was. Make sure all links work, and everything makes sense with JavaScript disabled on your page. This could also be the subject of a whole post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;6-make-javascript-run-last&quot;&gt;6. Make JavaScript Run Last&lt;&#x2F;h2&gt;
&lt;p&gt;JavaScript is the single slowest part of any page, and while it&#x27;s loading in the browser it &amp;quot;blocks&amp;quot; the browser from doing anything else. Therefore, if you&#x27;ve made a website that already works without JavaScript, just make JavaScript run last. This way, your users can start interacting with the page right away, and the page will get better once it&#x27;s fully loaded. Here&#x27;s what to do:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Move all JavaScript code to the bottom of the page, right before the closing &lt;code&gt;&amp;lt;&#x2F;body&amp;gt;&lt;&#x2F;code&gt; tag.&lt;&#x2F;li&gt;
&lt;li&gt;Move all that code into a separate JS file, and point a &lt;code&gt;&amp;lt;script&amp;gt;&lt;&#x2F;code&gt; tag at the new JS file&lt;&#x2F;li&gt;
&lt;li&gt;Surround all your JavaScript with an onload callback function (such as jQuery&#x27;s &lt;code&gt;$(document).ready()&lt;&#x2F;code&gt; or the built in javascript &lt;code&gt;window.onload=function(){}&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Combine all javascript files into one file if possible (jQuery and plugins, facebook, googleplus, your own code, etc). Be careful about the order of things! For example, make sure that jQuery is added before any jQuery plugins.&lt;&#x2F;li&gt;
&lt;li&gt;Experiment with different orders to see what feels the fastest&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you did steps 5 and 6, you&#x27;ll start to notice your page &lt;em&gt;feels&lt;&#x2F;em&gt; much much more responsive already. Well done! This is the hardest part.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;7-minify-css-and-js&quot;&gt;7. Minify CSS and JS&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve used &lt;a href=&quot;http:&#x2F;&#x2F;refresh-sf.com&#x2F;yui&#x2F;#output&quot;&gt;YUI CSS Compressor&lt;&#x2F;a&gt; and been happy with my compressed css. Once you&#x27;re happy, upload it to your own CDN from step 1. Alternatively, if you&#x27;ve started using LESS, SASS, or Stylus, these all can minify the css they produce for you easily. Consult the appropriate documentation for details.&lt;&#x2F;p&gt;
&lt;p&gt;Personally, I like using Google Closure Compiler to minify my JS. I&#x27;ve setup a batch script that includes all my JS files in a specific order and compresses it into a slightly smaller output file. That should be the subject of a whole separate post later, but suffice it to say that you can minify using &lt;a href=&quot;http:&#x2F;&#x2F;closure-compiler.appspot.com&#x2F;home&quot;&gt;closure compiler online&lt;&#x2F;a&gt; too.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;just-the-beginning&quot;&gt;Just the Beginning&lt;&#x2F;h2&gt;
&lt;p&gt;There&#x27;s a lot that could be said about &lt;a href=&quot;http:&#x2F;&#x2F;compass-style.org&#x2F;help&#x2F;tutorials&#x2F;spriting&#x2F;&quot;&gt;image sprites using Compass&lt;&#x2F;a&gt;, and I&#x27;ve only scratched the surface of JavaScript optimizations that can be made with social media. YSlow also offers plenty of other advice which is likely to help you a lot.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Pitfalls while Optimizing JavaScript</title>
		<published>2013-04-30T00:00:00+00:00</published>
		<updated>2013-04-30T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/pitfalls-while-optimizing-javascript/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/pitfalls-while-optimizing-javascript/</id>
		<content type="html">&lt;p&gt;I&#x27;ve tried to incorporate some best practices from &lt;em&gt;JavaScript Patterns&lt;&#x2F;em&gt; by Stoyan Stefanov of Yahoo! Press. Here&#x27;s some pitfalls I&#x27;ve run into and some simple fixes. The examples are straight JavaScript, no libraries used at all. However, the concepts and techniques could easily be applied to jQuery, Dojo, or other libraries, especially the for-loop section.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;variable-hoisting&quot;&gt;Variable Hoisting&lt;&#x2F;h2&gt;
&lt;p&gt;JavaScript essentially moves any variable declarations into the top of the current function (or global namespace), regardless of it&#x27;s placement in the source code. There are some edge cases where this makes variables be undefined or have confusing values. There&#x27;s some possible value in having all variables defined in the same place in your code (from a readbility perspective). If you&#x27;re working with hundreds of lines of code, or you have a very complex app you&#x27;re building, this is definitely a best practice. For short snippets of code, this is probably more optional. However, I&#x27;ll be following this practice in the sections to follow. There&#x27;s no real pitfall here, just an FYI!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;optimizing-for-loops&quot;&gt;Optimizing For-Loops&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;cache-the-length-property&quot;&gt;Cache the &lt;code&gt;length&lt;&#x2F;code&gt; property&lt;&#x2F;h3&gt;
&lt;p&gt;An easy way to speed up JavaScript code is to optimize for loops. The &lt;code&gt;length&lt;&#x2F;code&gt; property of arrays is actually more like a function call in that the browser will have to rexamine the array each time your script accesses the &lt;code&gt;length&lt;&#x2F;code&gt;. The way to optimize this situation is to read the length of the array once, store it in a local variable, and then compare the iterator to the local variable. Here&#x27;s an example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;myArray &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;7&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;9&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;max &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;myArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.length;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;max&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;++){
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;myArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;])
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Simple, right? Using &lt;code&gt;max&lt;&#x2F;code&gt; prevents the browser from reexamining &lt;code&gt;myArray&lt;&#x2F;code&gt;&#x27;s contents on every iteration of the loop, thus avoiding unnecessary work. This effect becomes more pronounced on larger arrays. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;count-down-not-up&quot;&gt;Count Down, Not Up&lt;&#x2F;h3&gt;
&lt;p&gt;While the first example above is all well and good, we could go a step farther. All else being equal, it&#x27;s faster to compare a number to 0 than to another number. So, we can (in theory) speed up this for-loop just a little bit by starting &lt;code&gt;i&lt;&#x2F;code&gt; at the length, and comparing to 0 and decrementing &lt;code&gt;i&lt;&#x2F;code&gt; instead. The added bonus is that we get to remove an extra variable. Here&#x27;s an example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;antipattern
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;myArray &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;7&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;9&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;myArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.length;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;--){
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;myArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;])
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that we&#x27;re still caching the &lt;code&gt;length&lt;&#x2F;code&gt; inside a variable. The odd thing about this syntax is that we set &lt;code&gt;i&lt;&#x2F;code&gt; outside the for-loop and thus the first statement is just a semicolon. While strange to see, you&#x27;d have to admit that the code inside in the for loop couldn&#x27;t get much shorter.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zero-relative-issues-with-arrays&quot;&gt;Zero-Relative Issues with Arrays&lt;&#x2F;h3&gt;
&lt;p&gt;If you try to run the second example above, it won&#x27;t work. Why not? The length property is 1 relative, but the array indices are zero relative. So, for instance &lt;code&gt;myArray[1]&lt;&#x2F;code&gt; would be &lt;code&gt;2&lt;&#x2F;code&gt;. The very first iteration of the loop, the script tries access a non-existent element in the array since it uses the &lt;code&gt;length&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The solution is simple. Set &lt;code&gt;i&lt;&#x2F;code&gt; to length - 1, and then inside the for loop update the conditional to include 0. Here&#x27;s final code:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;myArray &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;7&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;9&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;myArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.length - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;--){
   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;myArray&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;])
}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;optimizing-dom-lookups&quot;&gt;Optimizing DOM Lookups&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;the-ethics-of-cloning&quot;&gt;The Ethics of Cloning&lt;&#x2F;h3&gt;
&lt;p&gt;You can speed up your script to updating larger swathes of the DOM at a time whenever the nodes you&#x27;re changing share a common &lt;code&gt;parentNode&lt;&#x2F;code&gt;. It may not be appropriate for all situations, but I feel like I&#x27;m always iterating over a list tag and updating each of the list items.&lt;&#x2F;p&gt;
&lt;p&gt;To implement this &amp;quot;pattern,&amp;quot; grab the element you want, clone it, modify it, and swap out the original with the clone. Let&#x27;s look at an example:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F;antipattern
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;old_node &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= document.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;getElementById&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;product-list&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;new_node &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;old_node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;cloneNode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lis &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;new_node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;getElementsByTagName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lis&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.length - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;--) {
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lis&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;].style.color = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;red&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;
}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;old_node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.parentNode.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;replaceChild&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;new_node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;old_node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;copy-the-dom-subtree-separately&quot;&gt;Copy the DOM Subtree Separately&lt;&#x2F;h3&gt;
&lt;p&gt;Unfortunately the above snippet doesn&#x27;t work as you&#x27;d expect. The problem is that the W3C specifies that &lt;code&gt;cloneNode&lt;&#x2F;code&gt; will only copy the dom object and it&#x27;s attributes. However, the innerHTML, any text inside or nodes underneath are &lt;strong&gt;NOT&lt;&#x2F;strong&gt; copied. Thus, you&#x27;d get an exact copy of that node but nothing else.&lt;&#x2F;p&gt;
&lt;p&gt;The solution is to copy the innerHTML property as well. Here&#x27;s my example (you&#x27;re welcome to improve upon it):&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;old_node &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= document.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;getElementById&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;product-list&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;new_node &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;old_node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;cloneNode&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(),
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lis &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= [],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;new_node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;innerHTML &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;old_node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;innerHTML&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lis &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;new_node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;getElementsByTagName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lis&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.length - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;--) {
  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lis&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;].style.color = &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;red&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;quot;;
}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;old_node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.parentNode.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;replaceChild&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;new_node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;old_node&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Feel free to share your thoughts!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Comparison of Drupal Modules for Responsive Images</title>
		<published>2013-04-14T00:00:00+00:00</published>
		<updated>2013-04-14T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/comparison-of-drupal-modules-for-responsive-images/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/comparison-of-drupal-modules-for-responsive-images/</id>
		<content type="html">&lt;p&gt;Drupal has a whole host of modules that are designed to make it easier for Drupal themers and site builders to create responsive images. In other words, these Drupal modules aim to make mobile browsers download smaller images (and load more quickly) while still serving up full sized images to laptops. The feature chart below is based on descriptions from the drupal responsive image module pages, and my own investigations. &lt;strong&gt;All modules are Drupal 7, unless otherwise noted.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;responsive-image-features-explanation&quot;&gt;Responsive Image Features Explanation&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Module Name&lt;&#x2F;strong&gt; - links to the module&#x27;s project page on Drupal.org.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Fallback?&lt;&#x2F;strong&gt; - What happens when JavaScript is disabled on the browser?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Affects Image Types&lt;&#x2F;strong&gt; - Descibes how images must be stored and retrieved from Drupal for this module to work on them. Does this module work on inline image tags, only image fields, or other types of images?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;3rd Party Libraries&lt;&#x2F;strong&gt; - Does this module require you to download and install code outside of Drupal.org?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Ease of Installation&lt;&#x2F;strong&gt; - &amp;quot;Easy&amp;quot; means simply turn on the module and possibly configure a few settings.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Integrates with&lt;&#x2F;strong&gt; - Lists key modules that tie into this module.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Maintenance Concerns&lt;&#x2F;strong&gt; - Issues to think about after initial setup of the module.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Reported Installs&lt;&#x2F;strong&gt; - How many sites report using this module accoding Drupal.org project page.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;comparison-of-features&quot;&gt;Comparison of Features&lt;&#x2F;h2&gt;
&lt;div class=&quot;scroll&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
         &lt;th&gt;Module Name&lt;&#x2F;th&gt;
         &lt;th title=&quot;What happens when JavaScript is disabled&quot;&gt;Fallback?&lt;&#x2F;th&gt;
         &lt;th title=&quot;Can this module make img tags inside responsive in node bodies? Or, only image fields?&quot;&gt;Affects Image Types&lt;&#x2F;th&gt;
         &lt;th title=&quot;Denotes any non-Drupal code that is required to run this module&quot;&gt;3rd Party Libraries&lt;&#x2F;th&gt;
         &lt;th&gt;Ease of Installation&lt;&#x2F;th&gt;
         &lt;th title=&quot;Other Modules that this module is intended to work with&quot;&gt;Integrates with&lt;&#x2F;th&gt;
         &lt;th&gt;Maintenance Concerns&lt;&#x2F;th&gt;
         &lt;th title=&quot;Number of sites that report using this module on Drupal.org at time of writing&quot;&gt;Reported Installs&lt;&#x2F;th&gt;
      &lt;&#x2F;tr&gt;
    &lt;&#x2F;thead&gt;
    &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;http:&#x2F;&#x2F;drupal.org&#x2F;project&#x2F;adaptive_image&quot;&gt;Adaptive Image&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;
      &lt;td&gt;??&lt;&#x2F;td&gt;
      &lt;td&gt;Fields only&lt;&#x2F;td&gt;
      &lt;td&gt;None&lt;&#x2F;td&gt;
      &lt;td&gt;Easy&lt;&#x2F;td&gt;
      &lt;td&gt;Image Styles&lt;&#x2F;td&gt;
      &lt;td&gt;None&lt;&#x2F;td&gt;
     &lt;td&gt;4000ish&lt;&#x2F;td&gt;
    &lt;&#x2F;tr&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code&gt;&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;&amp;lt;a href=&amp;quot;http:&#x2F;&#x2F;drupal.org&#x2F;project&#x2F;cs_adaptive_image&amp;quot;&amp;gt;Client Side Adaptive Image&amp;lt;&#x2F;a&amp;gt;&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;noscript tag sets a fallback image&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Fields only&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;None&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Easy&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Image Field&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;None&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;1500&amp;lt;&#x2F;td&amp;gt;
&amp;lt;&#x2F;tr&amp;gt;

&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;&amp;lt;a href=&amp;quot;http:&#x2F;&#x2F;drupal.org&#x2F;project&#x2F;ais&amp;quot;&amp;gt;Adaptive Image Styles&amp;lt;&#x2F;a&amp;gt;&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Original image&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Fields, and Inline Img tags&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;None&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Hard&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Image Styles, Media, WYSIWYG&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Patching .htaccess module after drupal core updates!&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;1700&amp;lt;&#x2F;td&amp;gt;
&amp;lt;&#x2F;tr&amp;gt;

&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;&amp;lt;a href=&amp;quot;http:&#x2F;&#x2F;drupal.org&#x2F;project&#x2F;resp_img&amp;quot;&amp;gt;Responsive Images&amp;lt;&#x2F;a&amp;gt;&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;??&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Fields only&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;&amp;lt;a href=&amp;quot;https:&#x2F;&#x2F;github.com&#x2F;filamentgroup&#x2F;Responsive-Images&#x2F;tree&#x2F;cookie-driven&amp;quot;&amp;gt;Responsive Image library&amp;lt;&#x2F;a&amp;gt;&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Moderate&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Image Styles&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;&amp;lt;strong&amp;gt;NOT ACTIVELY MAINTAINED&amp;lt;&#x2F;strong&amp;gt;&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;106&amp;lt;&#x2F;td&amp;gt;
&amp;lt;&#x2F;tr&amp;gt;

&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;&amp;lt;a href=&amp;quot;http:&#x2F;&#x2F;drupal.org&#x2F;project&#x2F;resp_img&amp;quot;&amp;gt;Responsive Images and Styles&amp;lt;&#x2F;a&amp;gt;&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;CSS Based &#x2F; JS independent&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Fields, Blocks, Entities textfields, Inline img tags, and more&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;None&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Easy to Moderate&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Image Styles, Context, Media, Picture, field_slideshow, colorbox, expire&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;Will be part of D8 Core, but breaks on Drupal 7.20+ without some TLC (see project page)&amp;lt;&#x2F;td&amp;gt;
  &amp;lt;td&amp;gt;1811&amp;lt;&#x2F;td&amp;gt;
&amp;lt;&#x2F;tr&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;tbody&gt;
  &lt;&#x2F;table&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;concluding-thoughts-on-responsive-images-and-drupal&quot;&gt;Concluding thoughts on Responsive Images and Drupal&lt;&#x2F;h2&gt;
&lt;p&gt;Given the set of features and integration with Drupal 8, I highly recommend going with the Responsive Images and Styles module listed above. It appears that it can do everything the other modules do and more, plus it&#x27;s not much harder to configure (depending on if you use the Media module). However, if you want something simple with a larger set of users, Adapative Image would be my second choice. &lt;&#x2F;p&gt;
&lt;p&gt;I would absolutely avoid Adaptive Image Styles given the hassle of potentially fixing the htaccess file every time you update Drupal core. I would also avoid the Responsive Images module since it is no longer maintained. &lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Removing a Drupal Site</title>
		<published>2013-04-11T00:00:00+00:00</published>
		<updated>2013-04-11T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/removing-a-drupal-site/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/removing-a-drupal-site/</id>
		<content type="html">&lt;p&gt;I run a lot of development sites on my web server, and after the sites launch I remove the development sites. However, running a simple &lt;code&gt;rm -rf drupal&#x2F;path&#x2F;&lt;&#x2F;code&gt; from the shell never works. There&#x27;s always a few files that linger. &lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; chmod 777 sites&#x2F;default sites&#x2F;default&#x2F;files sites&#x2F;default&#x2F;*.php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; rm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; -rf&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt; sites&#x2F;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;During site-install, Drupal 7 protects 4 key files by setting permissions to something like 500. All we have to do is change the permissions and then delete them. Once you&#x27;ve logged in SSH, and changed into your Drupal root directory, follow above steps.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Running PHP 5.2 and 5.3 on the Same Server</title>
		<published>2013-01-11T00:00:00+00:00</published>
		<updated>2013-01-11T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/running-php-5-2-and-5-3-on-the-same-server/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/running-php-5-2-and-5-3-on-the-same-server/</id>
		<content type="html">&lt;p&gt;In this tutorial, I&#x27;m going walk you through how to setup PHP 5.3 and PHP 5.2 (or other versions) on the same shared hosting account with HostGator. Obviously, having two separate servers would be the ideal scenario, but when budgets don&#x27;t allow for that, it is possible to run different PHP versions on the same server and even account. Onward!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;UPDATE MAY, 2014&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; -- &lt;em&gt;HostGator is now converting &lt;a href=&quot;http:&#x2F;&#x2F;support.hostgator.com&#x2F;articles&#x2F;hosting-guide&#x2F;hardware-software&#x2F;what-version-of-php-are-you-using&quot;&gt;all PHP to version 5.4.&lt;&#x2F;a&gt; This is awesome because 5.4 is significantly faster than previous versions and it comes with a development server, traits, JavaScript array notation&lt;&#x2F;em&gt; (ex: &lt;code&gt;[1,&amp;quot;string&amp;quot;,TRUE,[],...]&lt;&#x2F;code&gt;) &lt;em&gt;and loads of other goodies and security updates. Great job, HostGator!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;##Why???&lt;&#x2F;p&gt;
&lt;p&gt;I was running &lt;a href=&quot;http:&#x2F;&#x2F;www.drupal.org&quot;&gt;Drupal&lt;&#x2F;a&gt; as the CMS on my personal shared hosting account, and I&#x27;ve been playing with around with &lt;a href=&quot;http:&#x2F;&#x2F;github.com&#x2F;bcosca&#x2F;fatfree&quot;&gt;Fat Free Framework&lt;&#x2F;a&gt; for building a lightweight backend and RESTful API to a mobile app. Before dishing out the whopping $50&#x2F;month to rollout my app on a dedicated server, I just wanted to do some basic user testing early on in the process. So, to save some money, I decided to use my personal shared hosting account (with HostGator).&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, Fat Free Framework requires PHP 5.3+ to work it&#x27;s magic, but HostGator by default uses PHP 5.2. After puttering through their documentation, I discovered that PHP 5.3 comes pre-installed on all shared hosting. Hooray! However, none of the Zend extensions are compatible with HostGator&#x27;s PHP 5.3 setup, such as PDO, sqlite, pdo_mysql, and others. Here&#x27;s the rub -- Drupal &lt;strong&gt;depends&lt;&#x2F;strong&gt; on these modules being loaded. So how can we escape this from this most heart-rending dilemma ever seen???&lt;&#x2F;p&gt;
&lt;p&gt;##Two Php Configurations&lt;&#x2F;p&gt;
&lt;p&gt;Because HostGator uses suPHP by default, we can instruct suPHP to load different php.ini files inside an .htaccess file. So, once we know what directives need to be set for each platform, we can put a custom php.ini and custom .htaccess file in their directories. Since most .htaccess directives apply to all subdirectories, we only need to put these configuration files into the top directories that need PHP 5.3, and everywhere that PHP 5.2 or Zend is needed, we can just leave everything as-is. The end result will be that each project has exactly the environment it needs.&lt;&#x2F;p&gt;
&lt;p&gt;To illustrate, here&#x27;s how I&#x27;ve structured my home directory:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;home&#x2F;user&#x2F;
    php.ini &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# This is the default that comes with shared hosting. No need to change this!
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;public_html&#x2F; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Drupal lives in here. Uses PHP 5.2 with Zend modules by default 
    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;php53&#x2F;
         .htaccess &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Tells Apache to use PHP 5.3 in this folder and all sub-folders
         &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;php.ini &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Tells suPHP not to load Zend modules
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;###Htaccess File for PHP 5.3&lt;&#x2F;p&gt;
&lt;p&gt;So, let&#x27;s put together the htaccess file for PHP 5.3. First we need to instruct Apache to use PHP 5.3 in this directory and all other subdirectories:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;AddType application&#x2F;x-httpd-php53 .php
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That was easy, right? Okay, now let&#x27;s tell suPHP where to find our custom php.ini file:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;lt;IfModule mod_suphp.c&amp;gt;
  suPHP_ConfigPath &#x2F;home&#x2F;user&#x2F;php53
&amp;lt;&#x2F;IfModule&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;&#x2F;strong&gt; You&#x27;ll need to change the path to point to the directory where you&#x27;re custom php.ini will be. The example above uses the directory structure I&#x27;ve outlined earlier in the article. &lt;&#x2F;p&gt;
&lt;p&gt;###php.ini File for PHP 5.3&lt;&#x2F;p&gt;
&lt;p&gt;By default, HostGator puts a php.ini for you in your home directory. Copy that file into the directory where you want to use PHP 5.3; for this example that would be &lt;code&gt;&#x2F;home&#x2F;user&#x2F;php53&lt;&#x2F;code&gt;. Open this file in your favorite text editor, and comment out all the lines underneath &lt;code&gt;[Zend]&lt;&#x2F;code&gt;. For me, this happens starting on line 1125, but it may be different for you. To comment out a line, simply put a semicolon &lt;code&gt;;&lt;&#x2F;code&gt; in front of it.&lt;&#x2F;p&gt;
&lt;p&gt;Also, PHP 5.3 seems to require a timezone to be set in the php.ini file. Simply add this code to the very bottom (substituting the &amp;quot;&lt;code&gt;America&#x2F;Los_Angeles&lt;&#x2F;code&gt;&amp;quot; with your &lt;a href=&quot;http:&#x2F;&#x2F;php.net&#x2F;manual&#x2F;en&#x2F;timezones.php&quot;&gt;time zone&lt;&#x2F;a&gt;):&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[Date]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.timezone=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;quot;America&#x2F;Los_Angeles&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When you are finished, the end of your custom php.ini file should look like this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[Zend]

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;extension=magickwand.so 
;extension=imagick.so
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;;extension=mailparse.so

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;extension=pdo.so
;extension=pdo_sqlite.so
;extension=sqlite.so
;extension=pdo_mysql.so
;extension=uploadprogress.so
;extension=gnupg.so
;extension=mailparse.so
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;;extension=fileinfo.so

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;;extension=mongo.so
;extension=http.so
;extension=phar.so
;extension=&amp;quot;ixed.5.2.lin&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;;zend_extension=&amp;quot;&#x2F;usr&#x2F;local&#x2F;Zend&#x2F;lib&#x2F;Optimizer-3.3.9&#x2F;php-5.2.x&#x2F;ZendOptimizer.so&amp;quot;

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;[Date]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;date&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;.timezone=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;quot;America&#x2F;Los_Angeles&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;making-sure-it-works&quot;&gt;Making Sure It Works&lt;&#x2F;h2&gt;
&lt;p&gt;Once you&#x27;ve uploaded your custom .htaccess and php.ini to the proper folder, create a file called phpinfo.php, and add this to it: &lt;code&gt;&amp;lt;?php phpinfo(); ?&amp;gt;&lt;&#x2F;code&gt;. Upload this folder to a publicly viewable location inside &lt;code&gt;&#x2F;home&#x2F;user&#x2F;php53&lt;&#x2F;code&gt; and open it in your browser. Scanning through the output, you should have PHP 5.3 running, and you should find your custom php.ini mentioned as well. Once you&#x27;ve found this information, remove the phpinfo.php. You should now be ready to upload your PHP 5.3+ code into the folder.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading:&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;2271910&#x2F;edit-htaccess-to-load-php-extension&quot;&gt;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;2271910&#x2F;edit-htaccess-to-load-php-extension&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;support.hostgator.com&#x2F;articles&#x2F;specialized-help&#x2F;technical&#x2F;what-is-php-ini&quot;&gt;http:&#x2F;&#x2F;support.hostgator.com&#x2F;articles&#x2F;specialized-help&#x2F;technical&#x2F;what-is-php-ini&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;support.hostgator.com&#x2F;articles&#x2F;hosting-guide&#x2F;hardware-software&#x2F;php-5-3&quot;&gt;http:&#x2F;&#x2F;support.hostgator.com&#x2F;articles&#x2F;hosting-guide&#x2F;hardware-software&#x2F;php-5-3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Debugging A PHP Error</title>
		<published>2012-09-24T00:00:00+00:00</published>
		<updated>2012-09-24T00:00:00+00:00</updated>
		<link href="https://bryce.fisher-fleig.org/debugging-a-php-error/" type="text/html"/>
		<id>https://bryce.fisher-fleig.org/debugging-a-php-error/</id>
		<content type="html">&lt;p&gt;I classify any PHP error in two flavors: coding errors and design errors. Firstly, I&#x27;ll discuss what a coding error is, why it might occur, how to find the error, and common fixes. Secondly, I&#x27;ll compare and contrast with design errors, since they can be more subtle and complex.&lt;&#x2F;p&gt;
&lt;p&gt;##Coding Errors&lt;&#x2F;p&gt;
&lt;p&gt;Coding errors involved a problem with the actual code written -- for example a misspelled word, a missing semicolon at the end of a line, etc. When configured to do so, PHP can provide error messages when it encounters a coding error, but often times error messages are turned off. However, in order to solve coding errors, you will need to configure PHP to provide you with these error messages.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-are-error-messages-turned-off&quot;&gt;Why Are Error Messages Turned Off?&lt;&#x2F;h3&gt;
&lt;p&gt;For security. PHP error messages often display part of the PHP code used to operate your website, or provide detailed information about the inner workings of your code or your webserver. Online predators could use this information to infect your server, gain unauthorized access, do other unpleasant things. You can make your website considerable more secure from these kinds of risks by keeping PHP error messages hidden from view on any publicly available websites. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;finding-error-messages&quot;&gt;Finding Error Messages&lt;&#x2F;h3&gt;
&lt;p&gt;There are two ways to keep these error messages private, while still being able see the error messages yourself:&lt;&#x2F;p&gt;
&lt;h4 id=&quot;1-only-display-errors-on-a-private-server&quot;&gt;1. Only Display Errors on a Private Server&lt;&#x2F;h4&gt;
&lt;p&gt;Typically, you&#x27;ll have a &amp;quot;development&amp;quot; server on your personal computer or a company server. This server should not be reachable by the general public online. If you&#x27;re working alone, you&#x27;ll want to setup a development server on your computer using Apache, MySQL, and PHP. There are easy to install preconfigured webservers for Mac OSX (mamp) and Windows (xampp) available free online. Usually, php developers will do the early phases of programming on a development server before they are ready to show their website to the world. Then they move all the code to a public webserver once they are done with the initial programming.&lt;&#x2F;p&gt;
&lt;p&gt;To display error messages for a single file, include this code at the beginning of your code, right after &amp;lt;?php &lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;&amp;lt;?php
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;ini_set&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;display_errors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;,&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;On&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;&amp;#39;);

&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Your existing code below here
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h4 id=&quot;2-log-error-messages-on-a-public-website&quot;&gt;2. Log Error Messages on a Public Website&lt;&#x2F;h4&gt;
&lt;p&gt;PHP can be configured to write error messages to a file named error_log. It does not have a file name extension -- the full file name is error_log. Typically, this file will be created in the same directory that error occurred in. Error logs are usually hidden from public view, but they can be downloaded through FTP so that you can still read the error message. This way, you can keep your server a little more secure and still be able to know where PHP encountered an error.&lt;&#x2F;p&gt;
&lt;p&gt;To turn on error logging for your server, find your php.ini file (use phpinfo() to help you locate this file), and make sure there is a line in php.ini that has&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;log_errors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;On
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you see a line:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;&quot;&gt;
&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;log_errors&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c0c5ce;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;Off
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Change it to &amp;quot;log_errors=On&amp;quot;, and restart Apache.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;understanding-error-messages&quot;&gt;Understanding Error Messages&lt;&#x2F;h3&gt;
&lt;p&gt;There are several kinds of errors that you will run into if you work with PHP for any length of time.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;syntax-errors&quot;&gt;Syntax Errors&lt;&#x2F;h4&gt;
&lt;p&gt;The most common errors are syntax errors, and the most common among these are missing semicolons from the end of the previous line, or missing brackets, or missing parentheses. Here is a list of &lt;a href=&quot;http:&#x2F;&#x2F;php.net&#x2F;tokens&quot;&gt;all tokens that might occur in an error message&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;fixing-common-syntax-errors&quot;&gt;Fixing Common Syntax Errors&lt;&#x2F;h5&gt;
&lt;p&gt;PHP error messages always include a file name and a line number. Syntax usually occur on the previous line. Open the file name in error message, go to the line number, and look carefully at the previous line, making sure:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;There is a semicolon at the end of the line (if necessary)&lt;&#x2F;li&gt;
&lt;li&gt;All parentheses and brackets open and close properly&lt;&#x2F;li&gt;
&lt;li&gt;All single quotes and double quotes have a matching pair&lt;&#x2F;li&gt;
&lt;li&gt;Every if statement has a closing bracket&lt;&#x2F;li&gt;
&lt;li&gt;Every function has a closing bracket&lt;&#x2F;li&gt;
&lt;li&gt;Every class has a closing bracket&lt;&#x2F;li&gt;
&lt;li&gt;Every for, while, foreach, and switch statement has a closing bracket&lt;&#x2F;li&gt;
&lt;li&gt;Every part of an if statement has an opening and closing parenthesis&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If that list didn&#x27;t solve your problem, try commenting out the entire file, and uncomment line by line until you figure where the error is occurring.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;preventing-syntax-errors&quot;&gt;Preventing Syntax Errors&lt;&#x2F;h5&gt;
&lt;p&gt;Over time, you are unlikely to stop making this mistake. Therefore, most programmers like to use a text editor that has syntax highlighting so that you&#x27;ll see visual cues about syntax problems as you type. This way, you can notice these problems right away, instead of spending minutes or hours looking for them on your own. This kind of help will save you hours and hours of time, and make your coding experience much more enjoyable. Some tools automatically create matching pairs of brackets, parentheses, and quotation marks for you as you type. Other programs beep if you type a syntax error. &lt;&#x2F;p&gt;
&lt;p&gt;Personally, I&#x27;ve been VIm for years because it&#x27;s free to download and use, it&#x27;s crossplatform, it&#x27;s on every linux machine ever, and it has a very healthy ecosystem of users and plugins. VIm has plugins like &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;vim-syntastic&#x2F;syntastic&quot;&gt;syntastic&lt;&#x2F;a&gt;. No tool is perfect, but most text editors will help you spot 99% of these kind of syntax errors.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;design-errors&quot;&gt;Design Errors&lt;&#x2F;h2&gt;
&lt;p&gt;Design errors occur when the code simply does not behave as expected, but no error message comes from PHP. Typically this occurs when the programmer (you!) has a wrong understanding of how some piece of software works. This can be a much harder to problem to solve. I use three different tactics to overcome a design error. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-reread-the-documentation-carefully&quot;&gt;1. Reread the documentation carefully&lt;&#x2F;h3&gt;
&lt;p&gt;Often, I&#x27;ve not read the documentation slowly that explains the php code I&#x27;m using, or I&#x27;ve missed an important concept on how this software is supposed to work, or what feature it offers. All native php functions can be found by going to http:&#x2F;&#x2F;php.net&#x2F;function. For instance, the &lt;code&gt;var_dump&lt;&#x2F;code&gt; function&#x27;s documentation can be found at &lt;a href=&quot;http:&#x2F;&#x2F;php.net&#x2F;var_dump&quot;&gt;http:&#x2F;&#x2F;php.net&#x2F;var_dump&lt;&#x2F;a&gt;. Also, googling &amp;quot;how to do &lt;em&gt;something&lt;&#x2F;em&gt; using &lt;em&gt;something else&lt;&#x2F;em&gt;&amp;quot; can uncover unofficial documentation, or posting questions on Stack Exchange can uncover help from the community.&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes, documentation is out of date, incomplete, or just plain wrong. In these cases, it&#x27;s time to try something else. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-try-another-approach&quot;&gt;2. Try Another Approach&lt;&#x2F;h3&gt;
&lt;p&gt;Most of the time, you just want to accomplish some task and it doesn&#x27;t make sense to spend too much time figuring why something isn&#x27;t working, or how something new is supposed to work. If you&#x27;ve worked with PHP before, use an easier or simpler solution. Better yet use a technique or piece of software with which you&#x27;re more familiar. Playing to your strengths is a great way to increase your own productivity. &lt;&#x2F;p&gt;
&lt;p&gt;Sometimes you just have to get something very specific to work a certain way. In these cases try the next technique.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;3-verify-each-step-of-your-code&quot;&gt;3. Verify each step of your code&lt;&#x2F;h4&gt;
&lt;p&gt;Use the &lt;code&gt;var_dump()&lt;&#x2F;code&gt; or &lt;code&gt;exit()&lt;&#x2F;code&gt; function to see what&#x27;s happening at critical moments in your code, such as checking what arguments were passed to your function, or if a function is being called at all. This is best way to get familiar with a small section of code that is doing something strange. The trick is to &lt;em&gt;figure out where you STOP understanding&lt;&#x2F;em&gt; what&#x27;s going on, and try to test different variables and values in the code to understand what&#x27;s happening there. &lt;strong&gt;Don&#x27;t assume anything!&lt;&#x2F;strong&gt; Keep finding and figuring out roadblocks &lt;em&gt;one at a time&lt;&#x2F;em&gt; until you&#x27;ve worked through the problem.&lt;&#x2F;p&gt;
&lt;p&gt;There are several tools that can help you &amp;quot;step&amp;quot; through the code to check variables at point in the execution of a script. Perhaps the most popular tool is &lt;a href=&quot;http:&#x2F;&#x2F;xdebug.org&#x2F;index.php&quot;&gt;xdebug&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;While this is a great way to understand code, it&#x27;s very time consuming if you&#x27;re working with a large piece of software like WordPress, Joomla, or Drupal. However, if you&#x27;re a novice programmer or a PHP newbie, this is the best guide to learning PHP there is.&lt;&#x2F;p&gt;
</content>
	</entry>
</feed>
