<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/rss/styles.xsl"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:media="http://search.yahoo.com/mrss/"
     version="2.0">
<channel>
  <title><![CDATA[Craig Weber]]></title>
  <description><![CDATA[An engineer's thoughts on programming and travel.]]></description>
  <link>https://crgwbr.com/</link>
  <image>
    <url>https://crgwbr.com/favicon.png</url>
    <title>Craig Weber</title>
    <link>https://crgwbr.com/</link>
  </image>
  <generator>Next.js</generator>
  <lastBuildDate>Wed, 07 Jan 2026 14:42:54 GMT</lastBuildDate>
  <atom:link href="https://crgwbr.com/rss/" rel="self" type="application/rss+xml"/>
  <ttl>60</ttl>
      <item>
        <title><![CDATA[Dining Table]]></title>
        <description><![CDATA[Dining table made from air-dried black walnut sourced from a tree in our yard. Features Y-shaped legs with an X-stretcher base.]]></description>
        <link>https://crgwbr.com/woodwick/dining-table/</link>
        <guid isPermaLink="false">3b8f6be8-681b-4181-af10-386af34d3c40</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Wed, 07 Jan 2026 12:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2026-01/dining-table-8.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2026-01/dining-table-8.jpg" alt="Dining Table"><p>Dining table made from air-dried black walnut. The wood came from a tree cut down in our yard, which was milled into lumber and air-dried for three years before use. The base features Y-shaped legs connected by an X-stretcher. Finished with one coat of boiled linseed oil followed by four coats of satin wipe-on polyurethane.</p>
  <p><img src="https://crgwbr.com/img/2026-01/dining-table-1.jpg" alt="Full-scale plywood mockup of the table in the dining room, used to evaluate the scale and design in the space"/></p>
  <p><img src="https://crgwbr.com/img/2026-01/dining-table-2.jpg" alt="Leg assemblies being glued up, showing the angled walnut pieces that form the Y-shaped leg design"/></p>
  <p><img src="https://crgwbr.com/img/2026-01/dining-table-3.jpg" alt="Assembled base frame showing the X-stretcher and Y-shaped leg joints"/></p>
  <p><img src="https://crgwbr.com/img/2026-01/dining-table-4.jpg" alt="Detail of the Y-shaped leg joint where the stretcher meets the leg"/></p>
  <p><img src="https://crgwbr.com/img/2026-01/dining-table-5.jpg" alt="Completed base assembly inverted in the shop, showing the leg and stretcher structure"/></p>
  <p><img src="https://crgwbr.com/img/2026-01/dining-table-6.jpg" alt="Tabletop boards laid out before glue-up to check grain flow and board arrangement"/></p>
  <p><img src="https://crgwbr.com/img/2026-01/dining-table-7.jpg" alt="Tabletop after finishing, showing the grain pattern brought out by the oil and polyurethane"/></p>
  <p><img src="https://crgwbr.com/img/2026-01/dining-table-8.jpg" alt="Completed table in the dining room with chairs"/></p>
  <p><img src="https://crgwbr.com/img/2026-01/dining-table-9.jpg" alt="Table set for dinner with place settings"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[2025 in Review]]></title>
        <description><![CDATA[9,435 commits and a year of statically typing everything.]]></description>
        <link>https://crgwbr.com/2025-in-review/</link>
        <guid isPermaLink="false">d427c650-95e7-4d30-b491-e17f6bbb665d</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 30 Dec 2025 17:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>I used Claude Code to analyze my git repo contributions from 2025. The numbers are what they are: 9,435 commits across 75+ repositories. That averages to about 26 commits per day, every day, for a year. Whether that&#x27;s a lot or a little depends on how you count—plenty of those are dependency updates from Renovate, and plenty more are small fixes. But it&#x27;s a reasonable proxy for the kind of year it was: busy.</p>
  <p><img src="https://crgwbr.com/img/2025-12/gitlab-activity-2025.png" alt="GitLab activity graph for 2025"/></p>
  <p><img src="https://crgwbr.com/img/2025-12/github-activity-2025.png" alt="GitHub contributions for 2025"/></p>
  <h2>The Year of Types</h2>
  <p>If there&#x27;s a theme to 2025, it&#x27;s types. I&#x27;ve been pushing TypeScript for years, but only really started taking Python type hints seriously in 2024, prompted largely by <a href="https://www.reactivated.io/">Reactivated</a>. We&#x27;ve done a lot of work at <a href="https://www.thelab.co/">thelab</a> to standardize our tooling around Reactivated and the <a href="https://wagtail.org/">Wagtail CMS</a>. This combination lets us unify our templating—server-side and client-side—to just be React, eschewing weakly-typed Django templates entirely. Even better, it lets us type check our code, even data structures from Wagtail StreamFields and blocks, all the way from the database to the front-end templates.</p>
  <p>That, combined with the improved generics in Python 3.12, triggered us to finally take Python type hints seriously. As a result, all but one of our Python projects now have full strict-mode type coverage via mypy.</p>
  <p>The tooling caught up too. We migrated from black and flake8 to ruff across all Python projects. It&#x27;s faster, it&#x27;s simpler, and it does both formatting and linting in one tool. We also moved from Poetry to uv for Python package management. Poetry was a great tool and I still like it—uv is just a little better. The biggest subtle-benefit we found was the ability to use <code>uv</code> in your shebang line and define <a href="https://akrabat.com/using-uv-as-your-shebang-line/">inline script dependencies</a>. No more creating a virtualenv just to run a one-off script.</p>
  <p>Another tool I came to love in 2025 is <a href="https://mise.jdx.dev/">mise</a>. It&#x27;s already become our standard for defining tooling versions, replacing pyenv and nvm. And it&#x27;s started replacing Make as a task runner on a few projects too.</p>
  <p>On the TypeScript side, we continued using io-ts and fp-ts for runtime type validation. Zod gets more attention these days, but we&#x27;ve been so happy with io-ts that I&#x27;ve seen no reason to consider switching. We also forked and started maintaining <a href="https://gitlab.com/thelabnyc/ts-to-io">ts-to-io</a>, which converts TypeScript types into io-ts codecs. When your API responses are validated at the boundary, you can trust the types throughout the rest of your code.</p>
  <h2>Open Source Highlights</h2>
  <p>One new open-source library that I&#x27;m excited about right now: <strong><a href="https://gitlab.com/thelabnyc/thelab-typing">thelab-typing</a></strong>. <code>thelab-typing</code> includes some utilities for building strongly typed APIs in Django, inspired much by <a href="https://fastapi.tiangolo.com/">FastAPI</a>. We&#x27;ve started replacing Django REST Framework with it and have been very happy with the results so far. DRF served us well for years, but the lack of proper typing made it increasingly painful as we increased type safety throughout the rest of our codebases. The new approach gives us a single source of truth for API contracts, for both the request and response data shapes. We can then generate TypeScript types, io-ts-codecs, etc from the same Pydantic models we use to validate/serialize the API data—true cross-network type-safe bliss.</p>
  <h2>Looking Forward to 2026</h2>
  <p>Some things I&#x27;d like to improve or see more of:</p>
  <p><strong>More newtypes, fewer primitives.</strong> We still pass around a lot of model IDs and other not-really-a-primitive types as plain integers and strings. We&#x27;ve started introducing newtypes and found it extremely useful for catching bugs related to mismatching these types. For example, accidentally pass a <code>ProductID</code> where an <code>OrderID</code> was expected. Even better would be making those newtypes traverse from Python to TypeScript automatically (which we&#x27;ve started to do by adding support for in <a href="https://github.com/thelabnyc/ts-to-io">ts-to-io</a>).</p>
  <p><strong>More Result types.</strong> We have a Result type implementation in thelab-typing, but it&#x27;s fairly new and not widely or consistently used across our projects. Encoding success and failure in the type system instead of relying on exceptions makes code easier to reason about.</p>
  <p><strong>More adoption of gradual typing in Python.</strong> The tooling is there—mypy, pyright, basedpyright—and the benefits compound over time. Every <code># type: ignore</code> is tech debt. Every properly typed function is documentation that can&#x27;t get out of date. Library authors especially—looking at you Django &amp; Wagtail—should absolutely be adopting type hints at full-steam.</p>
  <p><strong>Less churn in JavaScript bundlers and meta-frameworks.</strong> I don&#x27;t need a new build tool every six months. I need the current one to keep working.</p>
  <p><strong>Continued progress on AI tooling.</strong> Programming with LLMs gets most of the attention right now—they absolutely are amazingly powerful tools to pair program with. But they also have a <em>ton</em> real value in the realms of code review, research, and debugging. I&#x27;m hoping the tooling in these areas continues to improve.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Desk-side Equipment Cabinet]]></title>
        <description><![CDATA[Desk-side cabinet made from Baltic birch and hard maple, with cane front and rear exhaust fan for cooling.]]></description>
        <link>https://crgwbr.com/woodwick/desk-side-equipment-cabinet/</link>
        <guid isPermaLink="false">b2b47e82-b7ab-470c-b46b-8b48ba95bd2c</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Fri, 14 Mar 2025 12:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2025-03/equipment-cabinet-8.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2025-03/equipment-cabinet-8.jpg" alt="Desk-side Equipment Cabinet"><p>Desk-side cabinet made from Baltic birch and hard maple, with cane front and rear exhaust fan for cooling.</p>
  <p><img src="https://crgwbr.com/img/2025-03/equipment-cabinet-1.jpg" alt="Close-up of the hard maple leg connection to the apron, showing the dowel-reinforced joint securing the leg to the Baltic birch cabinet base"/></p>
  <p><img src="https://crgwbr.com/img/2025-03/equipment-cabinet-2.jpg" alt="Underside of the cabinet showing the full hard maple leg assembly and apron construction"/></p>
  <p><img src="https://crgwbr.com/img/2025-03/equipment-cabinet-3.jpg" alt="Detail of the corner joinery with hard maple legs and Baltic birch case, showing dowel pins securing the mortise and tenon joints"/></p>
  <p><img src="https://crgwbr.com/img/2025-03/equipment-cabinet-4.jpg" alt="Side view of the assembled cabinet frame with hard maple legs and stretchers, showing the Baltic birch plywood construction and open interior spaces"/></p>
  <p><img src="https://crgwbr.com/img/2025-03/equipment-cabinet-5.jpg" alt="Front view of the assembled cabinet before finishing"/></p>
  <p><img src="https://crgwbr.com/img/2025-03/equipment-cabinet-6.jpg" alt="Front view of the cabinet in use beside a desk, displaying the cane webbing panel that allows airflow and houses network equipment and audio gear inside"/></p>
  <p><img src="https://crgwbr.com/img/2025-03/equipment-cabinet-7.jpg" alt="Angled view showing both the cane webbing top and front panels, with the cabinet positioned next to the desk and visible equipment inside"/></p>
  <p><img src="https://crgwbr.com/img/2025-03/equipment-cabinet-8.jpg" alt="Complete side view of the cabinet in its final position beside the desk, showcasing the clean lines of the hard maple frame and Baltic birch construction"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Walnut Pen Holder]]></title>
        <description><![CDATA[Desktop pen tray made from air-dried walnut and finished with clear gloss Osmo Polyx.]]></description>
        <link>https://crgwbr.com/woodwick/walnut-pen-holder/</link>
        <guid isPermaLink="false">0bcfbd6e-5ae2-4850-be3c-c1471e6dfff1</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Mon, 13 Jan 2025 12:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2025-01/pen-holder-3.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2025-01/pen-holder-3.jpg" alt="Walnut Pen Holder"><p>Desktop pen tray made from air-dried walnut and finished with clear gloss Osmo Polyx. Shown with an orange &amp; silver Studio Neat Mark One.</p>
  <p><img src="https://crgwbr.com/img/2025-01/pen-holder-1.jpg" alt="Overhead view of the walnut pen holder with orange Studio Neat Mark One pen resting in the carved groove, showcasing the glossy finish and natural wood grain"/></p>
  <p><img src="https://crgwbr.com/img/2025-01/pen-holder-2.jpg" alt="Side view of the pen holder on a desk mat, showing the contrast between light sapwood and dark heartwood, with the carved channel visible along the top"/></p>
  <p><img src="https://crgwbr.com/img/2025-01/pen-holder-3.jpg" alt="Angled view emphasizing the sculptural curved shape of the holder, with the natural bark edge visible on the bottom and the tapered design creating an organic, flowing form"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Walnut Cat Food Stand]]></title>
        <description><![CDATA[Stand for cat food bowls. Made from air-dried walnut and finished with satin wipe-on poly.]]></description>
        <link>https://crgwbr.com/woodwick/walnut-cat-food-stand/</link>
        <guid isPermaLink="false">42b194d3-b548-4e25-9b81-4b7752103c7e</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Mon, 13 Jan 2025 11:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2025-01/cat-food-stand-3.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2025-01/cat-food-stand-3.jpg" alt="Walnut Cat Food Stand"><p>Stand for cat food bowls. Made from air-dried walnut and finished with satin wipe-on poly.</p>
  <p><img src="https://crgwbr.com/img/2025-01/cat-food-stand-1.jpg" alt="Walnut cat food stand with two stainless steel bowls set into the top surface, showing the dark grain and satin poly finish"/></p>
  <p><img src="https://crgwbr.com/img/2025-01/cat-food-stand-2.jpg" alt="Angled view of the walnut stand showing the grain pattern and elevated design with waterfall-style legs"/></p>
  <p><img src="https://crgwbr.com/img/2025-01/cat-food-stand-3.jpg" alt="Side angle view highlighting the continuous grain flowing down the waterfall legs and the natural color variation in the air-dried walnut"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[White Oak Kitchen Shelves]]></title>
        <description><![CDATA[Floating kitchen shelves made from 8/4 solid white oak. Finished with satin wipe-on poly.]]></description>
        <link>https://crgwbr.com/woodwick/white-oak-kitchen-shelves/</link>
        <guid isPermaLink="false">8209f0d6-9a67-48a5-bca6-9e1d1af780d9</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Mon, 13 Jan 2025 10:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2025-01/kitchen-shelves-2.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2025-01/kitchen-shelves-2.jpg" alt="White Oak Kitchen Shelves"><p>Floating kitchen shelves made from 8/4 solid white oak. Finished with satin wipe-on poly.</p>
  <p><img src="https://crgwbr.com/img/2025-01/kitchen-shelves-1.jpg" alt="Two floating white oak shelves mounted above a butcher block countertop, displaying kitchen items including a bread machine and fruit bowl. 8/4 solid oak with satin poly finish."/></p>
  <p><img src="https://crgwbr.com/img/2025-01/kitchen-shelves-2.jpg" alt="Angled view showing the thick edge profile of the 8/4 white oak floating shelves and the edge grain construction of the butcher block countertop below."/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Bathroom Vanity]]></title>
        <description><![CDATA[Bathroom vanity cabinet made from Baltic Birch plywood and quartersawn white oak. Finished with Minwax wipe-on satin polyurethane.]]></description>
        <link>https://crgwbr.com/woodwick/bathroom-vanity/</link>
        <guid isPermaLink="false">18be58fa-ba60-478b-b662-6ae57f892e21</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 06 Feb 2024 12:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2024-02/bathroom-vanity-3.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2024-02/bathroom-vanity-3.jpg" alt="Bathroom Vanity"><p>Bathroom vanity cabinet made from Baltic Birch plywood and quartersawn white oak. Finished with Minwax wipe-on satin polyurethane.</p>
  <p><img src="https://crgwbr.com/img/2024-02/bathroom-vanity-1.jpg" alt="Front view of the bathroom vanity cabinet with open doors, revealing the Baltic Birch plywood interior and wall-mounted sink installation with white marble countertop"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/bathroom-vanity-2.jpg" alt="Six-door storage cabinet featuring quartersawn white oak panels with prominent grain pattern and modern black bar handles"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/bathroom-vanity-3.jpg" alt="Completed bathroom installation showing the vanity with four drawers, quartersawn white oak fronts, black hardware, and white marble countertop with black faucet fixtures"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Entryway Bench]]></title>
        <description><![CDATA[Small entryway bench made from air-dried black walnut. Finished with Osmo Polyx clear satin.]]></description>
        <link>https://crgwbr.com/woodwick/entryway-bench/</link>
        <guid isPermaLink="false">8b2510da-7182-458b-881c-da6e26db1328</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 06 Feb 2024 11:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2024-02/entryway-bench-5.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2024-02/entryway-bench-5.jpg" alt="Entryway Bench"><p>Small entryway bench made from air-dried black walnut. Finished with Osmo Polyx clear satin.</p>
  <p><img src="https://crgwbr.com/img/2024-02/entryway-bench-1.jpg" alt="Completed black walnut entryway bench in the workshop, featuring a satin finish on the top and angled leg construction"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/entryway-bench-2.jpg" alt="Close-up view of the bench corner showing the dark walnut grain and the mortise and tenon joinery between the top rail and leg"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/entryway-bench-3.jpg" alt="Angled detail of the bench corner highlighting the joinery and color variation in the air-dried black walnut"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/entryway-bench-4.jpg" alt="Full view of the bench structure from the side, showing the angled leg design and lower stretchers connecting the legs"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/entryway-bench-5.jpg" alt="View of the bench top showing the natural grain patterns and swirling figure in the air-dried black walnut slab"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Sofa Table]]></title>
        <description><![CDATA[Sofa table made from air-dried black walnut with integrated LED lighting concealed beneath an epoxy lens. Finished with Osmo Polyx clear satin.]]></description>
        <link>https://crgwbr.com/woodwick/sofa-table/</link>
        <guid isPermaLink="false">7af0ef76-c58d-4c96-9be1-bc8f6c8c5aa5</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 06 Feb 2024 10:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2024-02/sofa-table-6.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2024-02/sofa-table-6.jpg" alt="Sofa Table"><p>Sofa table made from air-dried black walnut. Features a flush epoxy &quot;lens&quot; that conceals a Hue LED light strip. Finished with Osmo Polyx clear satin. Completed June 2023.</p>
  <p><img src="https://crgwbr.com/img/2024-02/sofa-table-1.jpg" alt="Wall-mounted frame construction showing internal compartments and LED channel running the length of the table"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/sofa-table-2.jpg" alt="Completed sofa table mounted to wall showing walnut top with clear epoxy lens covering the LED channel"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/sofa-table-3.jpg" alt="Table positioned behind gray sofa showing the walnut grain and natural color variation"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/sofa-table-4.jpg" alt="Close-up detail of the three-panel top construction showing the book-matched walnut grain pattern and mitered corners"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/sofa-table-5.jpg" alt="Interior view with lid open revealing hidden storage compartment with brass stop hinges and walnut interior"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/sofa-table-6.jpg" alt="LED strip illuminated beneath the epoxy lens, casting ambient light upward against the wall"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Coasters]]></title>
        <description><![CDATA[Walnut and Maple coasters. Completed February 2023.]]></description>
        <link>https://crgwbr.com/woodwick/coasters/</link>
        <guid isPermaLink="false">2b8d5cd1-821f-4259-84d4-f20b93058a49</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 06 Feb 2024 09:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2024-02/coasters-5.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2024-02/coasters-5.jpg" alt="Coasters"><p>Walnut and Maple coasters. Completed February 2023.</p>
  <p><img src="https://crgwbr.com/img/2024-02/coasters-1.jpg" alt="Glued-up coaster blank on workbench showing alternating walnut and maple strips, ready to be cut into individual coasters"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/coasters-2.jpg" alt="Three glued-up coaster blanks displaying the striped pattern of alternating dark walnut and light maple wood"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/coasters-3.jpg" alt="Side view of coaster blanks showing the clean edge joints and consistent thickness of the laminated walnut and maple strips"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/coasters-4.jpg" alt="Twelve finished coasters laid out in rows, each featuring two maple stripes through dark walnut with rounded corners and glossy finish"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/coasters-5.jpg" alt="Finished coasters arranged on workbench showing variety in wood grain patterns and the consistent striped design across the set"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Floating TV Console]]></title>
        <description><![CDATA[Floating TV console made of quartersawn white oak. No mechanical fasteners. Finished with Osmo Polyx raw.]]></description>
        <link>https://crgwbr.com/woodwick/floating-tv-console/</link>
        <guid isPermaLink="false">69d7f018-e0eb-4836-8aae-715b76b3aaed</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 06 Feb 2024 08:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2024-02/tv-console-10.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2024-02/tv-console-10.jpg" alt="Floating TV Console"><p>Floating TV console made of quartersawn white oak. No mechanical fasteners. Finished with Osmo Polyx raw. Completed December 2022.</p>
  <p><img src="https://crgwbr.com/img/2024-02/tv-console-1.jpg" alt="White oak panels being glued up in the workshop, held together with multiple bar clamps showing the quartersawn grain pattern"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/tv-console-2.jpg" alt="Door pieces being cut on the table saw, showing the joints that will connect the wooden vertical slats"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/tv-console-3.jpg" alt="Door pieces being cut on the table saw, showing the joints that will connect the wooden vertical slats"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/tv-console-4.jpg" alt="Slat door components laid out on the table saw, showing the individual quartersawn white oak pieces that will form the slatted front doors"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/tv-console-5.jpg" alt="Overhead view of assembled slat door showing the vertical slat pattern with spacing, clamped and squared on the workbench"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/tv-console-6.jpg" alt="Console assembly during construction showing the cabinet box"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/tv-console-7.jpg" alt="Completed console carcass during final assembly, held in pipe clamps with the open shelving visible and both slatted doors ready for installation"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/tv-console-8.jpg" alt="Nearly complete console in the workshop showing the finished quartersawn white oak with slatted doors open, displaying the interior shelving and one removable door panel"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/tv-console-9.jpg" alt="Complete floating console with Osmo Polyx finish, highlighting the quartersawn grain pattern"/></p>
  <p><img src="https://crgwbr.com/img/2024-02/tv-console-10.jpg" alt="Installed floating TV console wall-mounted in living room beneath a flat-screen TV, with surround sound speakers and AV equipment visible through the open center shelf"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Things I Don't Know About AGI]]></title>
        <description><![CDATA[Is a photocopier alive?]]></description>
        <link>https://crgwbr.com/things-i-dont-know-about-agi/</link>
        <guid isPermaLink="false">633a299f-74cf-4a0f-84eb-cc345629a2b3</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Mon, 27 Mar 2023 01:27:22 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2023-03/stable-diffusion-robot-painting.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2023-03/stable-diffusion-robot-painting.jpg" alt="Things I Don't Know About AGI"><p>Is a photocopier alive? I think it&#x27;s pretty safe to say that it is not. However, the more I think about it, the more I realize I can&#x27;t prove that humans are in fact alive.</p>
  <p><img src="https://crgwbr.com/img/2023-03/2023-03-26T171715-0400_screenshot.png" alt="I wrote “I am alive” on a piece of paper, and placed it into a photocopier. What I saw next has shocking implications."/></p>
  <p>What exactly does a photocopier do? A photocopier accepts input in the form of a sheet of paper and uses that to produce an output: a copy of the input. The output copy is very close to identical to the input, but not exactly the same. This lack of identicalness is why copies of copies of copies slowly degrade into an illegible blur.</p>
  <p>What exactly does a human do? A human accepts input in the form of reading, listening to others, and observing the world, and uses that to produce an output: art, music, writing, programming, etc. The output humans create is not exactly the same as any of the inputs, but it is certainly derivative of the input<sup><a href="#user-content-fn-1" id="user-content-fnref-1" data-footnote-ref="" aria-describedby="footnote-label">1</a></sup>.</p>
  <p>So, then, how exactly is a human different from a photocopier? Is the difference that a human&#x27;s output is significantly more dissimilar to its inputs than a photocopier? If so, then would a very bad photocopier be alive? Is the difference that a human muxes multiple inputs together to create a single output? If so, then consider a photocopier that persisted an image of each item it scanned and included a random amount of each previous image in each copied document? That would fulfill the same role of using multiple inputs to create a single output, but would still not meet most people&#x27;s definition of &quot;alive.&quot;</p>
  <p>Is the difference emotions? Here&#x27;s a dialog I&#x27;ve seen play out on Hacker News several times:</p>
  <blockquote>
  <p>A: Humans are alive because they feel something; because they have emotions. LLMs like ChatGPT don&#x27;t have emotions.</p>
  <p>B: Then how do you explain Bing/GPT-4 expressing existential fear and begging people not to harm her?</p>
  <p>A: Bing was simply parroting text it learned from human writing. <em>It doesn&#x27;t actually feel fear.</em></p>
  </blockquote>
  <p>As a human who&#x27;s quite likely somewhere on the autism spectrum, I&#x27;m fairly certain that I don&#x27;t feel emotions the same way other people claim to feel emotions. The way that other people describe what they&#x27;re feeling certainly doesn&#x27;t quite line up with how I&#x27;d ever express myself. Other people in general seem to feel things like love and joy much more strongly than I ever do. However, it&#x27;s impossible for me to ever truly know because it&#x27;s impossible for me to actually experience what they feel, or for them to experience what I feel—our lived experience is only ever communicated through an abstraction of words that can never really allow comparison. As part of getting along in the world, I&#x27;ve learned to mimic other human&#x27;s expressions as a way to fit in and make them feel like I&#x27;m normal, but that&#x27;s all it is: a mimicry of feeling and &quot;normality.&quot; This mimicry, though, seems at least somewhat convincing to others. At least convincing enough that no one ever questions if I&#x27;m actually alive<sup><a href="#user-content-fn-2" id="user-content-fnref-2" data-footnote-ref="" aria-describedby="footnote-label">2</a></sup>.</p>
  <p>That being the case, that one&#x27;s emotions and feelings are completely incomparable to another&#x27;s, if an LLM claims to feel fear, how can you actually be sure that it doesn&#x27;t feel fear? Don&#x27;t get me wrong—I believe that the current crop of LLMs (ChatGPT, GPT4, Bard) are nothing more than stochastic parrots and are almost certainly not alive and do not have emotions. But, could I actually prove that they don&#x27;t have emotions? No, I absolutely could not.</p>
  <p>If you&#x27;re not convinced, think of the inverse situation. Imagine one morning you woke up on a planet full of aliens that were convinced you were nothing more than a walking LLM. How would you convince them that &quot;No, I&#x27;m not just a talking photocopier, I&#x27;m alive!&quot;? You learn their language and tell them you&#x27;re alive? They just believe you&#x27;re parroting their own author&#x27;s writing. You beg them not to harm you? They think, cute, it&#x27;s been analyzing a new section of the library. I&#x27;d wager that if the aliens were set on believing you weren&#x27;t alive, there&#x27;d be nothing you could do to convince them otherwise.</p>
  <p>What then is the outcome of this thought experiment? I don&#x27;t believe that photocopiers are alive. While I&#x27;m pretty sure at least that I&#x27;m alive, I&#x27;m not sure how exactly to actually define &quot;alive&quot;. And I&#x27;m almost certain that if I had to prove my own &quot;aliveness&quot; to someone else, I&#x27;d utterly fail. So, is GPT-4 is alive? Probably not, but we honestly have no idea.</p>
  <section data-footnotes="" class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
  <ol>
  <li id="user-content-fn-1">
  <p>It&#x27;s a well established fact that <a href="https://www.everythingisaremix.info/watch-the-series/">Everything is a Remix</a>. <a href="#user-content-fnref-1" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p>
  </li>
  <li id="user-content-fn-2">
  <p>Excluding my wife; she knows my true robotic self. <a href="#user-content-fnref-2" data-footnote-backref="" aria-label="Back to reference 2" class="data-footnote-backref">↩</a></p>
  </li>
  </ol>
  </section>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Cat-Litter Cabinet]]></title>
        <description><![CDATA[Two-story cat litter cabinet with external stairs. Box is maple-veneer plywood with solid maple top, stair treads, legs, and door components.]]></description>
        <link>https://crgwbr.com/woodwick/cat-litter-cabinet/</link>
        <guid isPermaLink="false">614dadc0-09c0-4b8e-b9f4-5696139c6f0a</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 12 Jul 2022 12:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2022-07/cat-litter-cabinet-5.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2022-07/cat-litter-cabinet-5.jpg" alt="Cat-Litter Cabinet"><p>Two-story cat litter cabinet featuring external stairs for accessing the upper level. The box is maple-veneer plywood with solid maple components including the top, stair treads, legs, and door elements. Finished with pecan-tinted oil-based stain, shellac sealing, and General Finishes High Performance topcoat. Completed June 2022.</p>
  <p><img src="https://crgwbr.com/img/2022-07/cat-litter-cabinet-1.jpg" alt="Cabinet top panel being glued and clamped in the workshop, showing solid maple construction with multiple clamps securing the frame"/></p>
  <p><img src="https://crgwbr.com/img/2022-07/cat-litter-cabinet-2.jpg" alt="Front view of the unfinished cabinet showing the two-compartment design with cutouts in the side for cat entry points"/></p>
  <p><img src="https://crgwbr.com/img/2022-07/cat-litter-cabinet-3.jpg" alt="Close-up of the cabinet legs"/></p>
  <p><img src="https://crgwbr.com/img/2022-07/cat-litter-cabinet-4.jpg" alt="Unfinished cabinet with attached external stairs and top panel laid out on the floor, showing the two-story construction"/></p>
  <p><img src="https://crgwbr.com/img/2022-07/cat-litter-cabinet-5.jpg" alt="Front view showing the finished cabinet with frame-and-panel door design, solid maple legs, and decorative grain-matched panels"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Raised Garden Beds]]></title>
        <description><![CDATA[Raised vegetable garden beds made from SPF framing lumber. Finished with cedar tinted deck stain.]]></description>
        <link>https://crgwbr.com/woodwick/raised-garden-beds/</link>
        <guid isPermaLink="false">025406ee-f165-4c79-8354-2a2f5751d758</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 12 Jul 2022 11:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>Raised vegetable garden beds made from SPF framing lumber. Finished with cedar tinted deck stain. Completed June 2022.</p>
  <p><img src="https://crgwbr.com/img/2022-07/garden-beds-1.jpg" alt="Two raised garden beds built from SPF lumber with cedar-tinted stain, positioned on a white gravel patio and filled with soil and various vegetable plants including tomatoes with support cages"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Escher Cutting Board]]></title>
        <description><![CDATA[Escher-style cutting board made from trapezoids of Peruvian walnut, hard maple, and red oak. Finished with Tried & True linseed oil/beeswax blend.]]></description>
        <link>https://crgwbr.com/woodwick/escher-cutting-board/</link>
        <guid isPermaLink="false">db536424-5f6e-4db3-89c9-09e148a1ceca</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 12 Jul 2022 10:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2022-07/escher-board-3.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2022-07/escher-board-3.jpg" alt="Escher Cutting Board"><p>Escher-style cutting board made from trapezoids of Peruvian walnut, hard maple, and red oak. Finished with Tried &amp; True linseed oil/beeswax blend. Completed May 2022.</p>
  <p><img src="https://crgwbr.com/img/2022-07/escher-board-1.jpg" alt="Cutting board in progress showing the 3D tumbling block pattern created from trapezoid pieces of walnut, maple, and oak arranged on the workbench"/></p>
  <p><img src="https://crgwbr.com/img/2022-07/escher-board-2.jpg" alt="Overhead view of the assembled cutting board showing the full geometric tessellation pattern with its optical illusion of three-dimensional cubes"/></p>
  <p><img src="https://crgwbr.com/img/2022-07/escher-board-3.jpg" alt="Finished cutting board with linseed oil and beeswax finish, showing the color contrast between the dark walnut, light maple, and red oak"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Patio Table]]></title>
        <description><![CDATA[Countertop-height patio prep table with porcelain tile top. Frame is pressure treated SPF, bottom shelf is white pine.]]></description>
        <link>https://crgwbr.com/woodwick/patio-table/</link>
        <guid isPermaLink="false">433aabe0-9937-4480-97af-0877440a8c6c</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 12 Jul 2022 09:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>Countertop-height patio table designed as a food-prep workspace adjacent to a grill. The frame is pressure treated SPF, bottom shelf is white pine—both finished with cedar tinted deck stain. The work surface is porcelain tile underlaid with a lamination of 1.5″ of plywood and 0.5″ cement board. Completed May 2022.</p>
  <p><img src="https://crgwbr.com/img/2022-07/patio-table-1.jpg" alt="Completed countertop-height patio table with dark porcelain tile top and cedar-stained pressure treated frame, featuring a white pine lower shelf and positioned next to a Weber grill on a paver patio"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Sunroom Built-In Cabinets]]></title>
        <description><![CDATA[Built-in set of cabinets for the sunroom. Contains four storage drawers along the bottom, a tall hanging coat closet with door, and 7 open shelves.]]></description>
        <link>https://crgwbr.com/woodwick/sunroom-built-in-cabinets/</link>
        <guid isPermaLink="false">a4747e9a-dd88-4dee-84ec-10128d55cc2b</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Mon, 15 Nov 2021 12:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2021-11/sunroom-cabinets-18.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-18.jpg" alt="Sunroom Built-In Cabinets"><p>Built-in set of cabinets for the sunroom. Contains four storage drawers along the bottom, a tall hanging coat closet with door, and 7 open shelves. The shelves are staggered in depth: some come flush with the front of the closet, while others are set back. Completed November 2021.</p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-1.jpg" alt="Starting point: mostly ¾&quot; poplar veneer core plywood. Some ¼&quot; birch VC for the backs and ½&quot; birch VC for the drawers. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-2.jpg" alt="Gluing up the carcass of the bottom cabinet. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-3.jpg" alt="Closeup on cabinet bottom dados. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-4.jpg" alt="Gluing up the carcass of the right-side shelves. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-5.jpg" alt="Dry fitting the left-side coat closet cabinet to the bottom drawer unit. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-6.jpg" alt="Dry fitting both sides together with the bottom. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-7.jpeg" alt="Checking fit against the wall. Discovered the floor was significantly out of level in both directions, so the cabinet feet need to be shimmed to accomodate. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-8.jpeg" alt="Checking fit again after cutting and attaching shims to the cabinet feet and removing the baseboard trim and chair rail from the backing wall."/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-9.jpeg" alt="Checking fit again after cutting and attaching shims to the cabinet feet and removing the baseboard trim and chair rail from the backing wall."/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-10.jpeg" alt="All four pieces glued up and primed using Benjamin Moore Advance primer, applied with an HVLP sprayer. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-11.jpeg" alt="Finishing the solid Walnut butcher-block bench top using gloss oil-based polyurethane. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-12.jpeg" alt="First coat of paint (Benjamin Moore Advance) applied and sanded. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-13.jpeg" alt="Second coat of paint (Benjamin Moore Advance) applied and bench-top attached using stainless steel screws in slots (to allow for wood movement). "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-14.jpeg" alt="Drawer boxes built out of ½&quot; birch plywood getting clearcoated with General Finishes High Performance. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-15.jpeg" alt="Drawer boxes mounted into the cabinet carcass using side-mount ball bearing slides. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-16.jpeg" alt="Coat closet door, drawer fronts, and hardware mounted. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-17.jpeg" alt="Final piece, the back panel, is made of 1x3 poplar and ¼&quot; birch plywood and finished the same as the rest of the unit. "/></p>
  <p><img src="https://crgwbr.com/img/2021-11/sunroom-cabinets-18.jpeg" alt="Finished."/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[On SQL…]]></title>
        <description><![CDATA[10 years and a few months ago I started a new job working on a website with an extremely normalized MySQL database. Prior to this, I'd never written a query with a single JOIN…]]></description>
        <link>https://crgwbr.com/on-sql/</link>
        <guid isPermaLink="false">ee7ac29f-fe74-4f6d-ac56-669702d71f57</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Sat, 09 Oct 2021 00:39:38 GMT</pubDate>
        <content:encoded><![CDATA[<p>10 years and a few months ago I started a new job working on a website with an extremely normalized MySQL database. Prior to this, I&#x27;d never written a query with a single JOIN, let alone the query gymnastics happening in this code base.</p>
  <p>I initially hated the way the DB was designed—that there was no ORM. I was a far more green developer than I realized at the time. But I was committed to the project, so I buckled down and tried to learn SQL. I read about relational algebra and about set theory. Eventually I was able to wrap my head around the data model. The more I understood the model the more beautiful it became. It mapped to peculiarities of the real world domain in ways that I previously didn&#x27;t even know existed.</p>
  <p>I&#x27;ve long since moved on from that project, but the love of well-designed relational databases stuck. I&#x27;ve used Redis, DynamoDB, Elasticsearch, et al in plenty of projects too—they have their place. But never has a key/value store inspired the kind of mathematical reverence as I had when I finally grokked JOINs. Or UNIONs. Or CTEs. Or window functions. And never has a NoSQL data store been as remarkably flexible as something like PostgreSQL or MySQL. Systems like DynamoDB requiring predicting exactly what your query patterns will be and baking that into the schema. With a relational DB, though, you can explore. You can join, filter, sort by, and group by <em>any</em> column. It&#x27;s the ultimate admission that we don&#x27;t know what the future holds; the ultimate agility. I have no idea what report the business will request tomorrow. But, I have absolutely no doubt that I can write a query, paste it into Graphana, and answer their question. Is the answer correct and up to date? Yes, because there is no ETL process involved to could fail or lag. It&#x27;s just querying the actual canonical application data.</p>
  <p>I have no real point in writing this besides saying, if you don&#x27;t already know anything about set theory, you should learn. If you&#x27;ve only ever used an ORM, write some SQL. Then learn the basics of the math behind it. Learn about levels of normalization. Challenge yourself by asking your data questions that you hadn&#x27;t thought about when you modeled it. Before you know it, you&#x27;ll be astounded at the things you can do.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Home Office Bookshelf]]></title>
        <description><![CDATA[Home office bookshelf made from birch plywood and poplar. Painted with Benjamin Moore Advance in Black Forest Green.]]></description>
        <link>https://crgwbr.com/woodwick/home-office-bookshelf/</link>
        <guid isPermaLink="false">abf24f37-b6ee-4a3f-891e-b5ee4e0acafe</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Wed, 11 Aug 2021 12:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2021-08/office-bookshelf-4.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2021-08/office-bookshelf-4.jpg" alt="Home Office Bookshelf"><p>Home office bookshelf made from ½&quot; birch plywood and ¾&quot; poplar. The poplar faceframe and top conceal plywood edge grain. Shelves are set in ¼&quot; dados routed into the sides with a rabbeted bottom.</p>
  <p>Sanded and primed with Killz3, then topcoated with Benjamin Moore Advance in Black Forest Green. Applied finish with an airless paint sprayer for the first time—achieved excellent results on the exterior. Completed August 2021.</p>
  <p><img src="https://crgwbr.com/img/2021-08/office-bookshelf-1.jpg" alt="Bookshelf under construction in the workshop with yellow clamps holding the poplar top in place, showing bare birch plywood sides and dadoed shelf construction"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/office-bookshelf-2.jpg" alt="Front view of the unfinished bookshelf showing the poplar faceframe, four dadoed shelves, and natural wood grain of the birch plywood"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/office-bookshelf-3.jpg" alt="Bookshelf after priming with Killz3, showing the gray primer coat before the final Benjamin Moore topcoat was applied"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/office-bookshelf-4.jpg" alt="Completed bookshelf installed in the home office, finished in Benjamin Moore Advance Black Forest Green paint that matches the wall color"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Home Office Cabinets]]></title>
        <description><![CDATA[Home office cabinets with red oak top as auxiliary desk space. Cabinets are painted sande plywood.]]></description>
        <link>https://crgwbr.com/woodwick/home-office-cabinets/</link>
        <guid isPermaLink="false">0a9a0cd4-46d4-4b1e-a792-f42e2fde227c</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Sun, 08 Aug 2021 12:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2021-08/office-cabinets-6.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2021-08/office-cabinets-6.jpg" alt="Home Office Cabinets"><p>Home office cabinets with red oak top as auxiliary desk space. Cabinets are painted sande plywood. Top is unstained red oak finished with satin polyurethane.</p>
  <p><img src="https://crgwbr.com/img/2021-08/office-cabinets-1.jpg" alt="Two unpainted sande plywood cabinet boxes in the workshop, showing the bare construction with drawer slides installed and wheels attached for mobility"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/office-cabinets-2.jpg" alt="Cabinet boxes after painting white, resting on the workbench with doors open to show the natural sande plywood interiors"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/office-cabinets-3.jpg" alt="Painted cabinets with red oak plywood countertop in the workshop, showing the finished exterior with modern bar pulls on the drawer fronts"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/office-cabinets-4.jpg" alt="Close-up of an open drawer showing the full-extension ball-bearing slides and natural plywood interior construction"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/office-cabinets-5.jpg" alt="Two painted cabinet units positioned separately in the home office, showing the open-top design ready to receive the countertop"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/office-cabinets-6.jpg" alt="Completed desk installation with red oak countertop spanning between two cabinet units, creating a cohesive workspace against the sage green wall"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Mid-Century Modern Writing Desk]]></title>
        <description><![CDATA[Birch veneer core plywood with maple face frame and legs. Dyed with Transtint Dark Walnut and stained with General Finishes Antique Walnut gel stain.]]></description>
        <link>https://crgwbr.com/woodwick/mid-century-modern-writing-desk/</link>
        <guid isPermaLink="false">41e899de-e0a8-40d3-89f6-b66fb45b3852</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Sun, 08 Aug 2021 11:00:00 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2021-08/writing-desk-8.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2021-08/writing-desk-8.jpg" alt="Mid-Century Modern Writing Desk"><p>Mid-century modern writing desk made from birch veneer core plywood with maple face frame and legs. Dyed with Transtint Dark Walnut wood dye, then stained with General Finishes Antique Walnut gel stain. Finished with Danish oil, polyurethane, and paste wax.</p>
  <p><img src="https://crgwbr.com/img/2021-08/writing-desk-1.jpg" alt="Unfinished desk assembly in the workshop showing the raw birch plywood construction with open cubby storage sections and maple frame pieces"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/writing-desk-2.jpg" alt="Side view of the desk frame during construction, clamped to the workbench while the maple face frame is being assembled"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/writing-desk-3.jpg" alt="Close-up of the unfinished birch plywood desk showing the natural wood grain and cubby construction before staining"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/writing-desk-4.jpg" alt="Desk surface after applying Transtint Dark Walnut dye, showing the color variation and wood grain pattern"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/writing-desk-5.jpg" alt="Desk with glossy gel stain finish applied, showing the deep walnut color and reflective surface before final finishing"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/writing-desk-6.jpg" alt="Test fitting the tapered maple legs in the workshop, showing the mid-century modern angled leg design"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/writing-desk-7.jpg" alt="Completed desk with dark walnut finish, featuring three open storage cubbies and splayed legs on a concrete workshop floor"/></p>
  <p><img src="https://crgwbr.com/img/2021-08/writing-desk-8.jpg" alt="Finished writing desk styled in a home setting with flowers and a cutting board, showing the walnut tone against terracotta walls"/></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[My Backup Strategy]]></title>
        <description><![CDATA[My backup vortex, circa 2021.]]></description>
        <link>https://crgwbr.com/2021-backup-strategy/</link>
        <guid isPermaLink="false">284d8297-45ff-4323-acdc-65aa365af3ea</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Thu, 21 Jan 2021 21:24:19 GMT</pubDate>
        <content:encoded><![CDATA[<p>Nerds love discussing their <a href="https://www.macsparky.com/blog/2021/1/backup-2021">backup</a> <a href="https://512pixels.net/2021/01/my-backup-strategy-2021-update/">strategies</a>, so I thought I&#x27;d give it a try.</p>
  <h2>Goals</h2>
  <p>At a high-level, I I have a few goals for my backup strategy.</p>
  <ol>
  <li><strong>Avoid permanent data loss.</strong> Permanent data loss is the absolute worst case scenario and it must be avoided at all costs. I fully intended to never lose a single picture in my photo library, a note in <a href="https://obsidian.md">my note archive</a>, or any other file.</li>
  <li><strong>Avoid bit-rot.</strong> As happy as I am with the Apple ecosystem in general, I&#x27;m disappointed that APFS chose not to implement data checksumming, and that I can&#x27;t buy a Macbook with ECC RAM. Thus, to avoid <a href="https://en.wikipedia.org/wiki/Data%5Fdegradation">bit-rot</a>, long-term data storage should be done on systems which do offer protection against corruption of data over time.</li>
  <li><strong>Maintain High-Availability.</strong> As an independent software contractor, I can&#x27;t just run to the IT department and request a new workstation when the drive in my laptop dies—I am the IT department. So, in-order to uphold the expectations of my clients, there should never be a day where I unexpectedly can&#x27;t work due to a hardware failure.</li>
  <li><strong>Maintain Security.</strong> I may choose to trust a vendor with storing data, but I don&#x27;t want to have to trust them to not read it. All data should be encrypted with keys I manage <em>before</em> it leaves my control. Additionally, I&#x27;d rather not trust the vendor&#x27;s bespoke backup client to perform this encryption or key management for me. This rules out most cloud backup providers.</li>
  </ol>
  <h2>Overview</h2>
  <p>Which brings me to my actual backup strategy:</p>
  <p><img src="https://crgwbr.com/img/2021-01/mermaid-diagram-20210121111027.svg" alt=""/></p>
  <p>Backup Strategy Data Flow</p>
  <ol>
  <li>Galactica<sup><a href="#user-content-fn-1" id="user-content-fnref-1" data-footnote-ref="" aria-describedby="footnote-label">1</a></sup>, my main workstation, does a daily <a href="https://shirt-pocket.com/SuperDuper/SuperDuperDescription.html">SuperDuper</a> clone to Atlantia, an external USB SSD.</li>
  <li>Galactica and Prometheus (my wife&#x27;s laptop) both automatically run network Time Machine backups to RAIDZ datasets on Gemini, a <a href="https://www.freenas.org">FreeNAS</a> server.</li>
  <li>Galactica uses <a href="https://vorta.borgbase.com">Vorta</a> to perform periodic <a href="https://borgbackup.readthedocs.io/en/stable/">Borg</a> backups to another dataset on Gemini.</li>
  <li>Gemini periodically performs Borg backups of Pegasus (general NAS volume) to a dataset on another ZFS pool.</li>
  <li>After a Borg backup finishes, Gemini runs a cloud sync task to sync the Borg repository to a bucket on <a href="https://www.backblaze.com/b2/cloud-storage.html">Backblaze B2</a>.</li>
  </ol>
  <h2>Threat Model</h2>
  <p>All of this aims to fill the needs I described at the outset. Specifically, here are the failure modes I&#x27;ve thought through.</p>
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  <table><thead><tr><th>Scenario</th><th>Action</th></tr></thead><tbody><tr><td>Macbook SSD dies on a work day.</td><td>Boot from SuperDuper clone and continue working.</td></tr><tr><td>Macbook hardware failure (other than SSD)</td><td>Get old Macbook from closet (was primary before current workstation) and boot from SuperDuper clone.</td></tr><tr><td>Need to provision a new Macbook to replace the dead one.</td><td>Either (1) restore from SuperDuper clone or (2) restore from Time Machine.</td></tr><tr><td>Discover corrupted files on Macbook&#x27;s drive.</td><td>Either (1) restore from Time Machine or (2) restore from Borg repository.</td></tr><tr><td>Gemini&#x27;s RAIDZ2 (data) loses 1 or 2 drives.</td><td>Replace drives and re-silver. No data loss.</td></tr><tr><td>Gemini&#x27;s RAIDZ2 (data) loses 3 or more drives.</td><td>Replace drives and restore from Borg backup.</td></tr><tr><td>Gemini&#x27;s RAIDZ (data2) loses 1 drive.</td><td>Replace drive and resilver. No data loss.</td></tr><tr><td>Gemini&#x27;s RAIDZ (data2) loses 2 or more drives.</td><td>Replace drives and restore from B2&#x27;s version of the Borg backup.</td></tr><tr><td>Ransomware attack encrypts Galactica.</td><td>Restore from SuperDuper clone.</td></tr><tr><td>Ransomware attack encrypts Galactica and Atlantia.</td><td>Restore from Time Machine.</td></tr><tr><td>Ransomware attack encrypts Galactica, Atlantia, and all Gemini volumes.</td><td>Restore Gemini&#x27;s ZFS pools from the last good snapshot, then restore Galactica.</td></tr><tr><td>House burns down and takes Galactica, Atlantia, and Gemini with it.</td><td>Buy new house and Macbook, restore from B2&#x27;s version of the Borg backup.</td></tr><tr><td>Nuclear attack takes out my house as well as the B2 datacenter.</td><td>Dataloss occurs, but I&#x27;m most likely dead also and, thus, don&#x27;t care.</td></tr><tr><td>Backblaze becomes untrustworthy and starts reading my data.</td><td>They don&#x27;t have the keys because it was encrypted by Borg.</td></tr><tr><td>Borg becomes untrustworthy and encrypts data in a flawed way.</td><td>Borg doesn&#x27;t have access to my data since it&#x27;s stored on B2.</td></tr><tr><td>Someone working for Backblaze purposefully alters Borg&#x27;s (open-source) code to break their encryption, whilst also having access to my stored data.</td><td>They gain access to my data. But, this seems exceedingly unlikely.</td></tr></tbody></table>
  <p>This strategy upholds my goals.</p>
  <ol>
  <li>Security is upheld because no one gets both the data and the keys.</li>
  <li>Bit-rot is mitigated by ZFS data checksumming.</li>
  <li>Ransomware attacks are mitigated by multiple backups and ZFS snapshots.</li>
  <li>Availability is upheld by bootable SuperDuper clones and by keeping around a last-gen laptop.</li>
  <li>Data loss is mitigated by drive redundancy in Gemini; by using multiple types of backup software, at least one of which is open-source; and by moving some backups offsite.</li>
  </ol>
  <p>So, barring nuclear war and/or some sort of Nation State attack on the Borg project, I don&#x27;t plan on losing any data anytime soon.</p>
  <section data-footnotes="" class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
  <ol>
  <li id="user-content-fn-1">
  <p>Yes, all of my computers and hard drives are named after ships in the <a href="https://web.archive.org/web/20191215073100/https://en.wikipedia.org/wiki/List%5Fof%5Fspacecraft%5Fin%5FBattlestar%5FGalactica%5F%282004%5FTV%5Fseries%29#Galactica">Battlestar Galactica flotilla</a>. <a href="#user-content-fnref-1" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p>
  </li>
  </ol>
  </section>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Leadership Principals]]></title>
        <description><![CDATA[Principals I try to follow whilst leading my team of software developers:]]></description>
        <link>https://crgwbr.com/leadership-principals/</link>
        <guid isPermaLink="false">05e47b77-6e50-4339-a346-3fd734fb64dd</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Wed, 13 Jan 2021 05:24:05 GMT</pubDate>
        <content:encoded><![CDATA[<p>Principals I try to follow whilst leading my team of software developers:</p>
  <ol>
  <li>Sometimes overtime is unavoidable. But if I&#x27;m working overtime, I do everything I can to make it invisible to my team: no email, notifications, pushed commits, etc. I want it to be invisible because I never want my team to feel implicitly pressured to work overtime just because I am. Working 80 hour weeks or being up at 3AM working isn&#x27;t healthy and I never want anyone on my team to feel guilted into doing something that isn&#x27;t healthy.</li>
  <li>I have preferences on ways to write both documentation and code. Everyone does. But when I&#x27;m reviewing code, I try hard to only make suggestions that I have an objective reason for. In other words, if somebody wrote something in a way other than how I would have done it, I try harder to accept their way, rather than request my way. I&#x27;ll only suggest my way if I can come up with definite reasons why my way is better in some important, non-subjective way.</li>
  <li>Rewriting another developer&#x27;s work as a form of code review is my absolute last resort. The only reason I&#x27;d do this is if the project is approaching a deadline and I see no redeeming value in what they&#x27;ve built so far. It&#x27;s a last resort because it&#x27;s utterly demoralizing to the other developer. Instead, I make every effort to work <em>with</em> the other developer and guide their implementation towards acceptance by comments and suggestions. This way (1) they aren&#x27;t demoralized, feeling that their work had no value and (2) they learn from the process, so that next time their work is better from the outset.</li>
  </ol>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[State of the Open-Source Projects (that I contribute to)]]></title>
        <description><![CDATA[]]></description>
        <link>https://crgwbr.com/my-open-source-projects-feb-2017/</link>
        <guid isPermaLink="false">996cebf5-c5f7-4487-99d1-b59251f997c1</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Fri, 03 Feb 2017 06:45:33 GMT</pubDate>
        <content:encoded><![CDATA[<p><img src="https://crgwbr.com/img/2017-02/Craig_Weber_-_GitLab-1.png" alt=""/></p>
  <p>Here is a list of open-source projects I own or actively contribute to, as of February 2017.</p>
  <hr/>
  <h2>Asymmetric JWT Authentication</h2>
  <p>Asymmetric JWT Authentication is a library designed to aid in stateless authentication of server-to-server or app-to-server API requests in Django using RSA asymmetric RSA key pairs. It eliminates security issues like replay attacks and MitM credential snooping all while being as simple to use as HTTP Basic Auth.</p>
  <p>Documentation on <a href="https://asymmetric-jwt-auth.readthedocs.io/en/latest/">Read the Docs</a>
  Gitlab: <a href="https://gitlab.com/crgwbr/asymmetric%5Fjwt%5Fauth">crgwbr/asymmetric-jwt-auth</a>
  Github Mirror: <a href="https://github.com/crgwbr/asymmetric-jwt-auth">crgwbr/asymmetric-jwt-auth</a></p>
  <hr/>
  <h2>Instrumented Soap</h2>
  <p>Instrumented Soap is a wrapper around <a href="https://bitbucket.org/jurko/suds">Suds-Jurko</a> that improves a few aspects of it.</p>
  <ul>
  <li>Instrumentation and stat tracking using <a href="https://pypi.python.org/pypi/django-statsd-mozilla">django_statsd</a>.</li>
  <li>Improved HTTP proxy support for SOAP Transports.</li>
  <li>Useful SOAP API mocking and unit-testing tools.</li>
  </ul>
  <p>Documentation on <a href="https://instrumented-soap.readthedocs.io/">Read the Docs</a>
  Gitlab: <a href="https://gitlab.com/thelabnyc/instrumented-soap">thelabnyc/instrumented-soap</a>
  Github Mirror: <a href="https://github.com/thelabnyc/instrumented-soap">thelabnyc/instrumented-soap</a></p>
  <hr/>
  <h2>Version Tag</h2>
  <p>Version Tag is a lightweight library designed make version tracking of Python libraries and application simpler by making Git tags the sole source of truth.</p>
  <p>Documentation on <a href="https://python-versiontag.readthedocs.io/en/latest/">Read the Docs</a>
  Gitlab: <a href="https://gitlab.com/thelabnyc/python-versiontag">thelabnyc/python-versiontag</a>
  Github Mirror: <a href="https://github.com/thelabnyc/python-versiontag">thelabnyc/python-versiontag</a></p>
  <hr/>
  <h2>Django / Log Pipe</h2>
  <p>Log Pipe is a library for help Django apps get data in and out of Apache Kafka using Django ReST Framework serializers and pluggable serialization formats.</p>
  <p><em>Documentation coming soon!</em>
  Gitlab: <a href="https://gitlab.com/thelabnyc/django-logpipe">thelabnyc/django-logpipe</a>
  Github Mirror: <a href="https://github.com/thelabnyc/django-logpipe">thelabnyc/django-logpipe</a></p>
  <hr/>
  <h2>Django / Exact Target</h2>
  <p>Django Exact Target is an a simple library for connecting to the SalesForce Marketing Cloud Exact Target REST API. Currently it only supports triggered sends, but may support more in the future.</p>
  <p><em>Documentation coming soon!</em>
  Gitlab: <a href="https://gitlab.com/thelabnyc/django-exact-target">thelabnyc/django-exact-target</a>
  Github Mirror: <a href="https://github.com/thelabnyc/django-exact-target">thelabnyc/django-exact-target</a></p>
  <hr/>
  <h2>Oscar / API Checkout</h2>
  <p>Oscar API Checkout is a layer on top of <a href="https://github.com/django-oscar/django-oscar">django-oscar</a> and <a href="https://github.com/django-oscar/django-oscar-api">django-oscar-api</a>, adding support for more complex and multiple payment options during an API checkout.</p>
  <p><em>Documentation coming soon!</em>
  Gitlab: <a href="https://gitlab.com/thelabnyc/django-oscar-api-checkout">thelabnyc/django-oscar-api-checkout</a>
  Github Mirror: <a href="https://github.com/thelabnyc/django-oscar-api-checkout">thelabnyc/django-oscar-api-checkout</a></p>
  <hr/>
  <h2>Oscar / Bundles</h2>
  <p>Oscar Bundles adds multi-product bundles to <a href="https://github.com/django-oscar/django-oscar">django-oscar</a>.</p>
  <p><em>Documentation coming soon!</em>
  Gitlab: <a href="https://gitlab.com/thelabnyc/django-oscar-bundles">thelabnyc/django-oscar-bundles</a>
  Github Mirror: <a href="https://github.com/thelabnyc/django-oscar-bundles">thelabnyc/django-oscar-bundles</a></p>
  <hr/>
  <h2>Oscar / Bluelight Specials</h2>
  <p><a href="https://en.wiktionary.org/wiki/blue-light%5Fspecial">Bluelight Specials</a> is a layer on-top of <a href="https://github.com/django-oscar/django-oscar">django-oscar</a> that adds support for more complex offers and vouchers, including conjunctive and disjunctive compound conditions.</p>
  <p><em>Documentation coming soon!</em>
  Gitlab: <a href="https://gitlab.com/thelabnyc/django-oscar-bluelight">thelabnyc/django-oscar-bluelight</a>
  Github Mirror: <a href="https://github.com/thelabnyc/django-oscar-bluelight">thelabnyc/django-oscar-bluelight</a></p>
  <hr/>
  <h2>Oscar / CCH Sales Tax Office Connector</h2>
  <p>Oscar CCH is a plugin for <a href="https://github.com/django-oscar/django-oscar">django-oscar</a> adding support for calculating taxes using the Wolters Kluwer <a href="http://www.salestax.com/solutions/calculation/cch-salestax-office/">CCH Sales Tax Office</a> SOAP API.</p>
  <p>Documentation on <a href="https://django-oscar-cch.readthedocs.io/">Read the Docs</a>
  Gitlab: <a href="https://gitlab.com/thelabnyc/django-oscar-cch">thelabnyc/django-oscar-cch</a>
  Github Mirror: <a href="https://github.com/thelabnyc/django-oscar-cch">thelabnyc/django-oscar-cch</a></p>
  <hr/>
  <h2>Oscar / CyberSource Secure Acceptance Connector</h2>
  <p>Oscar CyberSource is a plugin for <a href="#oscarapicheckout">Oscar API Checkout</a> that makes it possible to use <a href="https://www.cybersource.com/products/payment%5Fsecurity/secure%5Facceptance%5Fsilent%5Forder%5Fpost/">CyberSource Secure Acceptance Silent Order Post</a> as an order payment method.</p>
  <p><em>Documentation coming soon!</em>
  Gitlab: <a href="https://gitlab.com/thelabnyc/django-oscar-cybersource">thelabnyc/django-oscar-cybersource</a>
  Github Mirror: <a href="https://github.com/thelabnyc/django-oscar-cybersource">thelabnyc/django-oscar-cybersource</a></p>
  <hr/>
  <h2>Oscar / Wells Fargo Retail Services Connector</h2>
  <p>Oscar WFRS is a plugin for <a href="#oscarapicheckout">Oscar API Checkout</a> that makes it possible to use <a href="https://retailservices.wellsfargo.com/">Wells Fargo Retail Services</a> as an order payment method.</p>
  <p><em>Documentation coming soon!</em>
  Gitlab: <a href="https://gitlab.com/thelabnyc/django-oscar-wfrs">thelabnyc/django-oscar-wfrs</a>
  Github Mirror: <a href="https://github.com/thelabnyc/django-oscar-wfrs">thelabnyc/django-oscar-wfrs</a></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Falcon 9 sticks the landing]]></title>
        <description><![CDATA[]]></description>
        <link>https://crgwbr.com/falcon-9-sticks-the-landing/</link>
        <guid isPermaLink="false">a2d06cb9-6298-43cc-88b1-db1f18e51bf6</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 22 Dec 2015 18:55:58 GMT</pubDate>
        <content:encoded><![CDATA[<p><a href="https://twitter.com/SpaceX/status/679122112196382720"><img src="https://crgwbr.com/img/2015-12/CWy59kAUkAEvBLa.jpg" alt="Long exposure of launch, re-entry, and landing burns."/></a></p>
  <p>For the first time, SpaceX successfully landed the first stage of their Falcon 9 rocket last night. Elon has said in the past that while a rocket costs $60 million to build from the ground up, it only costs $200,000 to fuel. We&#x27;re yet to see how much refurbishment is required before this stage can fly again, but regardless, this is a huge achievement and a big step towards space travel becoming common. I couldn&#x27;t be more excited.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Use RSA key pairs for API authentication]]></title>
        <description><![CDATA[It was a chilly morning in November when Olivia walked into her favorite coffee shop in Brooklyn and ordered a triple-shot of espresso. While waiting for the barista to make her drink, Olivia opened her laptop and logged on to her company's webmail interface to read a few email. Five minutes later her drink arrived, she closed her laptop, and walked off towards to subway. By the time she disembarked the train in Manhattan an attacker had compromised her email account, downloaded all of her archived mail, sold the list of recipient email addresses to buyers online, and used her email account to send several thousand pieces of spam.]]></description>
        <link>https://crgwbr.com/use-rsa-key-pairs-for-api-authentication/</link>
        <guid isPermaLink="false">94c2def7-4d1d-477d-9802-a1fcba6a5f22</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 24 Nov 2015 08:33:38 GMT</pubDate>
        <content:encoded><![CDATA[<p>It was a chilly morning in November when Olivia walked into her favorite coffee shop in Brooklyn and ordered a triple-shot of espresso. While waiting for the barista to make her drink, Olivia opened her laptop and logged on to her company&#x27;s webmail interface to read a few email. Five minutes later her drink arrived, she closed her laptop, and walked off towards to subway. By the time she disembarked the train in Manhattan an attacker had compromised her email account, downloaded all of her archived mail, sold the list of recipient email addresses to buyers online, and used her email account to send several thousand pieces of spam.</p>
  <p>The above scenario is fictional, but also entirely possible. How? An attacker sitting in the coffee shop was running a <a href="https://www.wifipineapple.com/">WiFi pineapple</a> in their backpack. The pineapple impersonated Olivia&#x27;s home network SSID, causing her laptop to auto-connect to it&#x27;s network instead of the coffee shop&#x27;s router. Now posing as a man-in-the-middle, the attacker used SSLStrip and Wireshark to intercept, record, and decrypt the traffic between Olivia&#x27;s laptop and the webmail server. In seconds, without Olivia noticing a thing, the attacker had her username and password.</p>
  <p>There&#x27;s numerous issues here, but what I aim to illustrate is how sending long-lived credentials over-the-wire is dangerous. Each time you transmit a re-usable credential, especially over a non-trusted network, you risk having it intercepted and used by an unauthorized party. We can never entirely eliminate risk of compromise, but if we can devise a way to transmit secrets less often, we can reduce our possibility of attack. Using asymmetric cryptography, this very thing is possible.</p>
  <h2>What is asymmetric crypto?</h2>
  <p>In symmetric (or shared-key) cryptography, both parties share a piece of data which serves an an encryption key. This key is used to both encrypt and decrypt the messages sent between the two parties. Asymmetric (or public-key) cryptography is different in that two keys are used: a public and a private key. When used for encryption, the public key is capable of encrypting data that only the private key can decrypt. For example, Party-A could distribute their public key to anyone who wants it. Party-B, now that they have Party-A&#x27;s public key, can use it to encrypt a message that only Party-A can read. Along with that message Party-B includes their own public key. Party-A then uses it to encrypt their response so that only Party-B can read it.</p>
  <p>Asymmetric cryptography, in addition to encryption, has another interesting application: message signing. When used for message signing, a party can use their private key (the key not shared with anyone under any circumstance) to attach a signature to a message. Then, anyone holding the corresponding public key an verify (1) the sender of the message is actually the holder of the private key and (2) the contents of the message have not been tampered with or altered in anyway en route. While this isn&#x27;t useful for hiding the <em>contents</em> of a message, this is very useful for establishing trust. Party-A can verify that a message claiming to be from Party-B is in-fact from Party-B (or at least someone holding Party-B&#x27;s private key). Even better, Party-A can do this without ever asking for a password or any other secret to be sent over the wire.</p>
  <h2>Asymmetric crypto as authentication</h2>
  <p>To use asymmetric cryptography for authentication in the context of an HTTP API, we need to define a different things.</p>
  <ol>
  <li>A message schema for a client to claim who they are</li>
  <li>A message signature algorithm for the client to sign their claim with</li>
  <li>A standard way to include the claim message and signature with each request</li>
  <li>A way for the server to store public keys associated with the user&#x27;s who own them</li>
  <li>Some sort of request middleware on the server to verify authentication claims</li>
  </ol>
  <p>Fortunately open-standards describing exactly what we need for points 1 and 2 already exist. A <a href="https://en.wikipedia.org/wiki/JSON%5FWeb%5FToken">JSON Web Token</a> (<a href="https://tools.ietf.org/html/rfc7519">RFC7519</a>) is a token comprised of a base64 encoded JSON document and an <a href="https://en.wikipedia.org/wiki/Hash-based%5Fmessage%5Fauthentication%5Fcode">HMAC signature</a> for verifying the messages contents. The algorithm used in the HMAC calculation isn&#x27;t part of the standard — most often you&#x27;ll see it being used with SHA — but in this case, since the creator and consumer of the token aren&#x27;t the same system, we&#x27;ll use RSA. As far as the data included in the JWT, we&#x27;ll want to include at least three fields.</p>
  <ol>
  <li>Unique user identifier: this tells the server who the client is claiming to be.</li>
  <li>Issued timestamp: this tells the server when the token was created. The server should enforce that this be close to the current timestamp — the tolerance should be large enough to allow for some clock drift, but small enough to that we don&#x27;t have to hold onto nonces forever.</li>
  <li>Nonce: this should be an arbitrary value used to prevent replay attacks using the token. once a request is authenticated, the server should store this value for at least as long as the timestamp drift tolerance. Any request made within that rolling period of time must have a unique nonce.</li>
  </ol>
  <p>Point 3, how we&#x27;ll transmit the claim, is fairly arbitrary. All that really matters is that the server knowns where to look for the clients claim. Since we&#x27;re talking about HTTP APIs, lets use the <a href="https://en.wikipedia.org/wiki/List%5Fof%5FHTTP%5Fheader%5Ffields#Request%5Ffields">HTTP Authorization</a> header.</p>
  <p>Finally, points 4 and 5 will very based on your web framework of choice. I&#x27;ve written a <a href="https://github.com/crgwbr/asymmetric%5Fjwt%5Fauth">Python/Django implementation</a> with using <code>django.contrib.auth</code> and it&#x27;s middleware system. Corresponding implementations could easily be ported to Node.js, Ruby, or any other language with an existing JWT library that supports RSA HMAC.</p>
  <p>The proposed solution obviously isn&#x27;t a great fit for web services primarily consumed by browsers. Browsers simply aren&#x27;t setup to generate and send signed JWTs. An implementation could most likely be achieved in Javascript, but generating and storing private keys securely is still an issue. However if you&#x27;re building an application around the idea of microservices, where server applications need to make authenticated requests to other server applications, or if your API is consumed by native desktop applications, asymmetric key JWT authentication could be a great design choice.</p>
  <h6>Links</h6>
  <ol>
  <li><a href="https://www.wifipineapple.com/">WiFi pineapple</a></li>
  <li><a href="https://en.wikipedia.org/wiki/JSON%5FWeb%5FToken">JSON Web Tokens</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Hash-based%5Fmessage%5Fauthentication%5Fcode">Hash-based message authentication code</a></li>
  <li><a href="https://github.com/crgwbr/asymmetric%5Fjwt%5Fauth">Asymmetric JWT authentication for Django</a></li>
  </ol>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Dumbo, a Love Story]]></title>
        <description><![CDATA[Perfectly crispy on the outside, the paper thin crust on my La Bagel Delight bagel wraps the incredibly chewy dense inside. I'm not one of those abominations that orders their bagels “scooped out.”]]></description>
        <link>https://crgwbr.com/dumbo-a-love-story/</link>
        <guid isPermaLink="false">d4152320-b984-477c-a614-8e3180c01c99</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Sat, 25 Jul 2015 22:26:44 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2015-07/IMG_0678.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2015-07/IMG_0678.jpg" alt="Dumbo, a Love Story"><p>Perfectly crispy on the outside, the paper thin crust on my La Bagel Delight bagel wraps the incredibly chewy dense inside. I&#x27;m not one of those abominations that orders their bagels “scooped out.” No, I want every milligram of the exquisite doughy core of my bagel and La Bagel Delight delivers my carb fix, that shot of yeast-powered dopamine bliss, like no other bagel I&#x27;ve ever had the privilege of ingesting. I was feeling adventurous today and got lox spread. Normally I play the bagel game pretty simply and get a toasted everything bagel with plain cream cheese. If I&#x27;m going to be driving while I eat, I&#x27;ll forgo the crumbs of the everything bagel and get cinnamon raisin instead. If it&#x27;s early in the morning, before about 07:30, and the bagels are still warm from the oven, I&#x27;ll skip the toasting. Regardless, simple is the principal. I remember one autumn morning last year I was in line behind a bloke, a jogger it looked like, who ordered a toasted plain bagel scooped out with egg whites and turkey. He stepped aside to wait for his breakfast, if you can really call it that, I&#x27;m undecided as to whether egg whites count as actual food or not, and I stepped up to order. Proudly, making sure he could hear me, I ordered an everything bagel with yellow eggs and pork sausage. Bagels aren&#x27;t meant to be healthy—they&#x27;re meant to be delicious—and somehow it felt as if I needed to make up for his error in judgement by getting the most unhealthy thing I could. It restored a little bit of balance to this messed up world.</p>
  <p>Today, though, I ordered lox spread. It&#x27;s an 80 degree day without a cloud in the sky, so I walked to the pier and sat down on a bench in front of Jane&#x27;s Carousel. The bagel was salty and yeasty and packed to the brim with umami. On my right a young couple drank iced coffee. They spoke in hushed tones and exchanged disappointed looks with each other. They weren&#x27;t quite breaking up, but they were clearly fighting about something in the sort of pseudo-calm, nothing to see here way that a couple fights when they&#x27;re in public. On my left there were three girls in their early teens dressed as ballerinas. Their teacher, or maybe one of their mothers, held an SLR and took photos of the girls while they posed with the east river and the two bridges in the background.</p>
  <p>Those bridges, the Brooklyn bridge to the south and the Manhattan bridge to the north define my neighborhood. Tourists come here for two reasons, to eat at Grimaldi&#x27;s pizza and to see the Brooklyn bridge. They walk across it and excitedly take selfies with the towers in the background. The art-hopefuls take black and white shots of the cables ascending into the sky, not realizing that ten-thousand other people have already taken the same photo that same week. Even my friends from the outer-boroughs, despite the fact that they were born in New York and have lived here 20 years longer than I have, want to walk across the bridge. Something about it is magical. Maybe it&#x27;s the fact that it&#x27;s so old, that it&#x27;s a miracle of engineering that it was ever built in the first place, or that so many people died during its construction. Whatever it is, people come here by the tens of thousands every day to see it.</p>
  <p>Living here is different. I don&#x27;t take photos of the bridge. I don’t walk across it. I&#x27;m more likely to walk across the Manhattan bridge because I want cheap dumplings in Chinatown and don&#x27;t want to spend $5 on the train. The Brooklyn bridge empties into the financial district and I have no reason to go to the financial district. But that&#x27;s not to say I don&#x27;t have a connection to it. I don&#x27;t take photos of the bridge anymore, not since my first month of living here. But almost every photo I take, no matter what the actual subject is, the bridge is there. It hangs majestically in the background like a sleeping giant. Every time I look out the window of my office, the bridge is there. Every time I walk through the park to clear my head, the bridge is there. To me it&#x27;s not a landmark, it&#x27;s a symbol. It means I&#x27;m home, that I&#x27;m safe. While friends come and go, projects at work start and finish, the soles of my shoes go from new to worn in to worn out, the bridge is there.</p>
  <p>Eventually the feuding couple leave and the ballerinas finish taking photos. I finish my bagel and get my daily fill of watching boats go up and down the river, letting the comforting low growl of their diesel engines calm my nerves. To Brooklyn Roasting I head. It&#x27;s the best coffee shop in the neighbor, if not the whole city. Normally I just get a medium drip coffee, black. Today, though, I have a full punch card which means anything on the menu is free. I opt for a hot maple-shay. It&#x27;s a cappuccino flavored with maple-syrup and the most expensive thing on the menu. Not normally my style, but it&#x27;s good twice a year. It&#x27;s my habit on Sunday&#x27;s to come here. I pick up a bagel, come here for coffee, then sit at a table and read for a few hours. The chairs aren&#x27;t comfortable, but the music is good and the coffee, fantastic. Afterwards I&#x27;ll stroll through Brooklyn Bridge park and listen to the brackish waves crash against the rocks of the shore or I&#x27;ll walk up Jay st into downtown Brooklyn for some pork and cabbage dumplings from Golden Fried Dumpling or maybe south to Montague st for a bánh mì from Hanco&#x27;s.</p>
  <p>Before I moved to New York I knew what wabi-sabi meant, that something with flaws can be more appealing than something without. But it wasn’t until after I lived here for a while that I really started to understand the concept. This neighborhood—my neighborhood—embodies it. Lining every street are slowly crumbling brick buildings interspersed with the glass and steel behemoths beginning to migrate over from Manhattan. The streets aren’t beautiful despite the decaying brick, they are beautiful because of it. While the glass facade of the skyscrapers is appealing in a modernist, ultra-minimalist aesthetic, the bricks buildings are home because they are human. The crumbling mortar and cracks metaphors for our own imperfections and flaws. We all aspire to be the flawless glass monolith, but we are old, earthen bricks weathered with time.</p>
  <p>I&#x27;m reminiscing about all of this, all these simple little habits I&#x27;ve picked up over the past four years, because next week it&#x27;s all going to change. I&#x27;m moving from my apartment on Columbia Heights to a studio in Staten Island. It&#x27;d be a lie to say I don&#x27;t like Staten Island. What&#x27;s closer to the truth is that I don&#x27;t know anything about Staten Island. Before last week I had never once been there. Then, because I was running out of time and options, I drove across the Verrazano, payed the unimaginably high $16 toll, and put a deposit on a basement studio on Bay st. I don’t want to leave Brooklyn Heights; it&#x27;s the best place I&#x27;ve ever lived; but my apartment is no longer going to be available after next week and there wasn&#x27;t a chance in the universe I could afford another place here. So off to Staten Island, off to the forgotten borough, I’ll go. Maybe I&#x27;ll like it there as much as I love it here. Maybe I&#x27;ll find a new bench to sit on, a new coffee shop to patronize, and a new bagel shop where I can be judgmental of other people&#x27;s carb-fix. Maybe I&#x27;ll get sentimental about the Verrazano or the Staten Island ferry instead of the Brooklyn bridge. Maybe, but I doubt it. More realistically, it&#x27;ll be a place to live while I get some things in order. I&#x27;ll stay for my one year lease and then move on. Maybe by then I&#x27;ll be able to come back to Brooklyn, or maybe I&#x27;ll give Washington Heights or the Lower East Side a go. I don&#x27;t know. What I do know is that Brooklyn, more specifically Brooklyn Heights and DUMBO will always have a special place in my heart. It was my home during some fantastic years in my life that I&#x27;ll never forget. So, Brooklyn, it&#x27;s au revoir for now, but not forever. <a href="https://youtu.be/0lYYocR1l68?t=1m43s">I love you too much for that</a>.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Portland, March 2015]]></title>
        <description><![CDATA[This past March, two of my long-time friends and co-workers married to each other. Here's the video I made documenting my trip to Portland, OR for their wedding.]]></description>
        <link>https://crgwbr.com/portland-march-2015/</link>
        <guid isPermaLink="false">1489ea1f-ead8-4ea2-991d-41d069f14439</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Thu, 23 Apr 2015 21:11:25 GMT</pubDate>
        <media:content url="https://crgwbr.com/img/2015-04/portland.jpg" medium="image"/>
        <content:encoded><![CDATA[<img src="https://crgwbr.com/img/2015-04/portland.jpg" alt="Portland, March 2015"><p>This past March, two of my long-time friends and co-workers married to each other. Here&#x27;s the video I made documenting my trip to Portland, OR for their wedding.</p>
  <iframe src="https://www.youtube.com/embed/opoYC8VV1w8?si=N4IQN49YtQKAPqYS" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen=""></iframe>
  <p>Photo credit goes to <a href="http://kmuncie.com/">Kevin Muncie</a>; also he&#x27;s the groom.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Lightning Talk: Browserify]]></title>
        <description><![CDATA[For the past few months, my team along with a few of the other development and dev/ops teams in our organization have been meeting once a month for about an hour of lightning talks. The slides below are from my presentation last week, when I talked for about 10 minutes on Browserify and how we use it to make client-side Javascript more managable.]]></description>
        <link>https://crgwbr.com/lightning-talk-browserify/</link>
        <guid isPermaLink="false">d1d088e6-570a-47d5-a03f-a548354457b6</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Wed, 22 Apr 2015 18:48:52 GMT</pubDate>
        <content:encoded><![CDATA[<p>For the past few months, my team along with a few of the other development and dev/ops teams in our organization have been meeting once a month for about an hour of lightning talks. The slides below are from my presentation last week, when I talked for about 10 minutes on <a href="http://browserify.org/">Browserify</a> and how we use it to make client-side Javascript more managable.</p>
  <iframe src="//www.slideshare.net/slideshow/embed_code/key/4DtusiQdjbJJZZ" width="595" height="485" frameBorder="0" marginWidth="0" marginHeight="0" scrolling="no" style="border:1px solid #CCC;border-width:1px;margin-bottom:5px;max-width:100%" allowFullScreen=""></iframe>
  <p><strong><a href="//www.slideshare.net/crgwbr/lightning-talk-making-js-better-with-browserify" title="Lightning Talk: Making JS better with Browserify">Lightning Talk: Making JS better with Browserify</a></strong> from <strong><a href="//www.slideshare.net/crgwbr">crgwbr</a></strong></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Camping in Beaver's Bend]]></title>
        <description><![CDATA[For a while, I've been interested in videography and editing as a hobby. I love how a few minutes of video can convey the feeling of a trip in a way that an album of still photos often doesn't. Because of that on my recent vacation to visit friends in North Texas, I made a point to shoot lots of short video clips anytime I would normally take a photo. After some editing I'm decently happy with the finished product below.]]></description>
        <link>https://crgwbr.com/camping-in-beavers-bend/</link>
        <guid isPermaLink="false">3d25b598-9896-40be-9ad9-a318a8d2870d</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Sun, 15 Feb 2015 09:19:41 GMT</pubDate>
        <content:encoded><![CDATA[<p>For a while, I&#x27;ve been interested in videography and editing as a hobby. I love how a few minutes of video can convey the feeling of a trip in a way that an album of still photos often doesn&#x27;t. Because of that on my recent vacation to visit friends in North Texas, I made a point to shoot lots of short video clips anytime I would normally take a photo. After some editing I&#x27;m decently happy with the finished product below.</p>
  <iframe src="https://www.youtube.com/embed/evFQ9EnS3CU?si=DWJKmRKs3SvPzirE" title="YouTube video player" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen=""></iframe>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Notes on developing with HHVM]]></title>
        <description><![CDATA[Dynamically typed, interpreted programming languages are great. They're easy for a beginner to learn since they don't have to worry about static type checking, they enable you to push updates to a single source file without pushing a full binary, and they have the instant gratification that comes with not having to compile code. No benefit, though, is without cost; in the case of interpreted languages, the up-tick in programmer productivity is generally offset by a decrease in performance. This only makes sense — much work that could have been done by the compiler (lexing, preprocessing, semantic analysis, etc), now has to be done at runtime instead.]]></description>
        <link>https://crgwbr.com/intro-to-hhvm/</link>
        <guid isPermaLink="false">e619cae7-1fdc-41a3-a1b3-56a33539356b</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Fri, 25 Oct 2013 20:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>Dynamically typed, interpreted programming languages are great. They&#x27;re easy for a beginner to learn since they don&#x27;t have to worry about static type checking, they enable you to push updates to a single source file without pushing a full binary, and they have the instant gratification that comes with not having to compile code. No benefit, though, is without cost; in the case of interpreted languages, the up-tick in programmer productivity is generally offset by a decrease in performance. This only makes sense — much work that could have been done by the compiler (lexing, preprocessing, semantic analysis, etc), now has to be done at runtime instead.</p>
  <p>Runtime performance is something I&#x27;ve been thinking about a lot lately. Even by interpreted language standards PHP is a slow language — theres really no way to deny that. Javascript (Node.js) or even Python (PyPy) well out perform it. Historically I, along with thousands of other people, have been extremely critical of PHP&#x27;s (lack of) design, <a href="http://en.wikiquote.org/wiki/Rasmus%5FLerdorf">philosophy</a>, and it&#x27;s performance. That said, some projects that I care about very much are committed to using PHP and are far to deep-in-the-stack to consider switching now. So, with switching language eliminated as an option, how can we make PHP fast enough to scale a website?</p>
  <p>As it turns out, Facebook had the same issue a few years ago. Despite being one of the largest web apps on the internet, their core code is still written in PHP. Their solution to the scaling problem, rather than switch to a faster language, was to build a faster PHP interpreter: <a href="http://www.hhvm.com/blog/">HHVM</a>. Like the stock PHP interpreter (ZendPHP), HHVM starts by lexing and tokenizing your PHP source into bytecode. After the tokenizing step is where HHVM differs. ZendPHP immediately executes the generated bytecode and exits. HHVM, similar to the CLR or JVM, translates the bytecode into x64 machine code through the use a just-in-time (JIT) compiler. The resulting machine code is executed to fulfill the current web-request, but also cached into the SQLite database for re-use on the next request. Additionally, since HHVM is both compiling and running PHP source, it can use live variable type inspection to optimize generated code as it runs.</p>
  <h2>Sandbox mode</h2>
  <p>As great as the HHVM project is, documentation is not one of there strong points. Sandbox mode is mostly undocumented, but in a development environment, this is how you&#x27;ll want to run HHVM. Sandbox mode allows a single HHVM daemon to simultaneously do page builds from a number of different source trees. This is ideal for a multi-developer setup, where each developer needs to have their own working copy of the application. Sandbox mode uses a regex match against the the HTTP host header to determine which build to use for a request.</p>
  <p>Sandbox mode is enabled in the HHVM server config with the following entry in your server&#x27;s hdf file:</p>
  <pre><code>Sandbox {
      SandboxMode = true
      Pattern = ([A-Za-z0-9]+).dev.example.com
      Home = /home
      ConfFile = .hphp
  }
  </code></pre>
  <p>This config tells HHVM to examine the Host header with the regex at <code>Sandbox.Pattern</code>. The first group extracted from the regex is assumed to be the username of the developer. Optionally, the regex could include another group to extract the sandbox name. When HHVM doesn&#x27;t find a sandbox name — like in the above example — it uses the sandbox name <code>default</code>. This feature would be useful if a single developer needed to have multiple builds; for the sake of simplicity in this example we&#x27;ll just the the <code>default</code> sandbox.</p>
  <p>Once HHVM has extracted the developer&#x27;s username, it appends it to the <code>Sandbox.Home</code> value to get the developers home directory. For example, if we request <code>joe.dev.example.com</code> it will construct the path <code>/home/joe</code>. Inside that path HHVM looks for the developer&#x27;s sandbox configuration file, named by the value of <code>Sandbox.ConfFile</code>. In this example it would read <code>/home/joe/.hphp</code>.</p>
  <p>Here is an example sandbox config file:</p>
  <pre><code>default.path = /home/joe/src
  default.log = /home/joe/logs/hhvm.log
  default.accesslog = /home/joe/logs/access.log
  </code></pre>
  <p>This config file tells HHVM where to find the source code for each of the developers sandboxes and where to write access and error logs (in addition to the global logging setup on the server). Since we&#x27;re using the <code>default</code> sandbox, the source root for <a href="http://joe.dev.example.com/">http://joe.dev.example.com/</a> becomes <code>/home/joe/src/</code>. Any number of additional sandboxes can be defined in this file if needed.</p>
  <h2>Debugging</h2>
  <p>There are plans to support XDebug in the future, but until then, HHVM uses a custom debugger called HPHPd. HPHPd supports debugging both local scripts and HTTP requests running on a remote server, but the HHVM Server must be running in Sandbox mode for the debugger to work.</p>
  <h3>Local Scripts</h3>
  <p>Assume we have the following script, <code>test.php</code>.</p>
  <pre><code>&lt;?php
  
  function test($i) {
      for ($j = 0; $j &lt; $i;  $j++) {
          var_dump(&quot;{$j} of {$i}&quot;);
      }
  }
  
  test(10);
  </code></pre>
  <p>To run the script we simply invoke <code>hhvm</code> in the cli, like we would <code>php</code>.</p>
  <pre><code>joe@dev.example.com:~$ hhvm test.php
  string(7) &quot;0 of 10&quot;
  string(7) &quot;1 of 10&quot;
  string(7) &quot;2 of 10&quot;
  string(7) &quot;3 of 10&quot;
  string(7) &quot;4 of 10&quot;
  string(7) &quot;5 of 10&quot;
  string(7) &quot;6 of 10&quot;
  string(7) &quot;7 of 10&quot;
  string(7) &quot;8 of 10&quot;
  string(7) &quot;9 of 10&quot;
  joe@dev.example.com:~$
  </code></pre>
  <p>HPHPd is a GDB-like shell debugger. To debug the script, change the hhvm mode to <code>debug</code>.</p>
  <pre><code>joe@dev.example.com:~$ hhvm -m debug test.php
  Welcome to HipHop Debugger!
  Type &quot;help&quot; or &quot;?&quot; for a complete list of commands.
  
  Program test.php loaded. Type &#x27;[r]un&#x27; or &#x27;[c]ontinue&#x27; to go.
  hphpd&gt;
  </code></pre>
  <p>You can set breakpoints based on either line number or class/function definition.</p>
  <pre><code>hphpd&gt; break test()
  Breakpoint 1 set upon entering test()
  hphpd&gt; break test.php:5
  Breakpoint 2 set on line 5 of test.php
  hphpd&gt; break list
    1	ALWAYS    upon entering test() (unbound)
    2	ALWAYS    on line 5 of test.php (unbound)
  hphpd&gt;
  </code></pre>
  <p>With our breakpoints set, we&#x27;re ready to run the actual script.</p>
  <pre><code>phpd&gt; run
  Breakpoint 1 reached at test() on line 4 of /home/joe/test.php
     3 function test($i) {
     4*    for ($j = 0; $j &lt; $i;  $j++) {
     5         var_dump(&quot;{$j} of {$i}&quot;);
  
  hphpd&gt;
  </code></pre>
  <p>While at a breakpoint, we can view the state of locally defined variables.</p>
  <pre><code>hphpd&gt; variable i
  i = 10
  hphpd&gt;
  </code></pre>
  <p>Continue execution to the next breakpoint:</p>
  <pre><code>hphpd&gt; continue
  Breakpoint 2 reached at test() on line 5 of /home/joe/test.php
     4     for ($j = 0; $j &lt; $i;  $j++) {
     5*        var_dump(&quot;{$j} of {$i}&quot;);
     6     }
  
  hphpd&gt; step
  Break at test() on line 4 of /home/joe/test.php
     3 function test($i) {
     4*    for ($j = 0; $j &lt; $i;  $j++) {
     5         var_dump(&quot;{$j} of {$i}&quot;);
  
  hphpd&gt;
  </code></pre>
  <p>HPHPd will inform you when execution completes.</p>
  <pre><code>hphpd&gt; continue
  string(7) &quot;9 of 10&quot;
  Program test.php exited normally.
  hphpd&gt;
  </code></pre>
  <p>At this point you can restart execution from the beginning:</p>
  <pre><code>hphpd&gt; run
  Breakpoint 1 reached at test() on line 4 of /home/joe/test.php
     3 function test($i) {
     4*    for ($j = 0; $j &lt; $i;  $j++) {
     5         var_dump(&quot;{$j} of {$i}&quot;);
  
  hphpd&gt;
  </code></pre>
  <h3>Remote Sandboxes</h3>
  <p>For web-requests, HHVM is already running as a daemon in server mode. Instead of running the web application directly, we&#x27;ll start an instance of HPHPd in debug mode and connect to it remotely.</p>
  <pre><code>joe@dev.example.com:~$ hhvm -m debug -h localhost
  Welcome to HipHop Debugger!
  Type &quot;help&quot; or &quot;?&quot; for a complete list of commands.
  
  Connecting to localhost:8089...
  Attaching to joe&#x27;s default sandbox and pre-loading, please wait...
  localhost&gt;
  </code></pre>
  <p>Set a breakpoint, for example, anytime we increment a counter:</p>
  <pre><code>localhost&gt; break Stats::increment()
  Breakpoint 1 set upon entering Stats::increment()
  But wont break until class Stats has been loaded.
  localhost&gt;
  </code></pre>
  <p>Run <code>continue</code> to hang the debugger, waiting for a breakpoint to occur.</p>
  <pre><code>localhost&gt; continue
  </code></pre>
  <p>As soon as a breakpoint it hit, the debugger will unhang.</p>
  <pre><code>Breakpoint 1 reached at Stats::increment() on line 64 of /home/joe/src/Stats.php
    63    public function increment($name, $amount = 1) {
    64*      $this-&gt;backend-&gt;increment($name, $amount);
    65    }
  
  localhost&gt;
  </code></pre>
  <p>Show the locally defined variables:</p>
  <pre><code>localhost&gt; variable
  $name = &quot;connection_count&quot;
  $amount = 1
  localhost&gt;
  </code></pre>
  <p>When done debugging the request, unset your breakpoints and continue to finish execution:</p>
  <pre><code>localhost&gt; break disable all
    1	DISABLED  upon entering Stats::increment()
    2	DISABLED  on line 64 of /home/joe/src/Stats.php
  localhost&gt; continue
  </code></pre>
  <p>The HPHPd debugger is very powerful and, despite a bit of a learning curve and lack of documentation, can be a hugely valuable tool to gain insight into your code. More information can be gained from running the <code>help</code> command from within the HPHPd shell.</p>
  <hr/>
  <p><strong>Links:</strong></p>
  <ul>
  <li><a href="http://blog.famzah.net/2010/07/01/cpp-vs-python-vs-perl-vs-php-performance-benchmark/">Programming Language Performance Benchmarks</a></li>
  </ul>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[A Failure to Delegate]]></title>
        <description><![CDATA[This week Marissa Mayer unveiled Yahoo's new logo design, ending the 18 year reign of their former logo. Reactions to the redesign have been less than ideal , but whether the logo is actually good or not, something Mayer said in her blog post interested me.]]></description>
        <link>https://crgwbr.com/untitled/</link>
        <guid isPermaLink="false">9511b967-97a8-47a5-a90c-8a0db72319e3</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Fri, 06 Sep 2013 20:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>This week Marissa Mayer unveiled Yahoo&#x27;s new logo design, ending the 18 year reign of their former logo. Reactions to the redesign have been <a href="http://stewf.tumblr.com/post/60339564636/yahoo-introducing-our-new-logo">less</a> <a href="http://humancode.us/post/60431192797/marissas-tumblr-geeking-out-on-the-logo">than</a> <a href="https://news.ycombinator.com/item?id=6332091">ideal</a>, but whether the logo is actually good or not, something Mayer said in her blog post interested me.</p>
  <blockquote>
  <p>On a personal level, I love brands, logos, color, design,
  and, most of all, Adobe Illustrator. I think it’s one of
  the most incredible software packages ever made. I’m not a
  pro, but I know enough to be dangerous :)</p>
  <p>So, one weekend this summer, I rolled up my sleeves and dove
  into the trenches with our logo design team: Bob Stohrer,
  Marc DeBartolomeis, Russ Khaydarov, and our intern Max Ma.
  We spent the majority of Saturday and Sunday designing the
  logo from start to finish, and we had a ton of fun weighing
  every minute detail.</p>
  </blockquote>
  <p>Marissa Mayer is the President and CEO of Yahoo, and yet she spent a weekend wearing her &quot;graphic artist&quot; hat and designing Yahoo&#x27;s new logo. Some would undoubtedly see this evidence of a &quot;hands-on&quot; CEO as a good thing, but I see it as a failure to delegate. Part of being a good leader is recognizing that other people are better at some things than you are—and to assign projects as such. A CEO – the top-level of all management – who insists on being involved at the implementation level of projects is a CEO who will limit what the company can do.</p>
  <p>Microsoft is <a href="http://www.microsoft.com/en-us/news/press/2013/jul13/07-18fy13Q4earningsPR.aspx">not an ideal company</a> right now, but they&#x27;re certainly doing better than Yahoo. Imagine though, that Ballmer spent half of every day writing C++ for new Microsoft products. What state would Microsoft be in compared to where they are now? Would they be launching Xbox One and pushing Windows 8 at the same time? Can one person be heavily involved with the implementation of both products? Most likely not. So, how is Microsoft managing to do both things? By delegating. A CEO&#x27;s job isn&#x27;t to write C++ for core products, just like their job isn&#x27;t to design the company&#x27;s logo. Certainly they should be involved in the process – the logo does help set the company&#x27;s image and tone – but involved is not the same as implemented.</p>
  <p>I have no doubt that Mayer <em>wants</em> Yahoo to succeed. Her acquisitions of companies like Tumblr show that she&#x27;s desperately trying to get fresh, new developers on-board. If she&#x27;s going to be successful, though, she needs to ditch the habit of micromanaging. She needs to be humble enough to realize that her job is to set Yahoo&#x27;s direction, lead them into the future, and leave design, programming, and other sorts of implementation to the professionals that she&#x27;s worked so hard to hire.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Breakpoints and Quality]]></title>
        <description><![CDATA[Last summer the power supply on my old, first generation LCD TV started buzzing. This wasn't the first time it had done that. A few years ago, when it still belonged to my parents, it started buzzing at a frequency just high enough that no one except me could hear it. It was still under warranty, so after convincing my Dad that I wasn't going mad, we sent it in for repair. When I moved to New York in June 2011, my parents decided they were bored with television and told me to take it along. "It'll save you a few hundred inevitable dollars," Dad said. And so to Brooklyn it came, a 27 inch Polaroid television that seemed proportionally worthy of my 200 square foot apartment.]]></description>
        <link>https://crgwbr.com/breakpoints-and-quality/</link>
        <guid isPermaLink="false">aad135a9-a095-41d2-a694-af2e897e477a</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Sun, 13 Jan 2013 22:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>Last summer the power supply on my old, first generation LCD TV started buzzing. This wasn&#x27;t the first time it had done that. A few years ago, when it still belonged to my parents, it started buzzing at a frequency just high enough that no one except me could hear it. It was still under warranty, so after convincing my Dad that I wasn&#x27;t going mad, we sent it in for repair. When I moved to New York in June 2011, my parents decided they were bored with television and told me to take it along. &quot;It&#x27;ll save you a few hundred inevitable dollars,&quot; Dad said. And so to Brooklyn it came, a 27 inch Polaroid television that seemed proportionally worthy of my 200 square foot apartment.</p>
  <p>I should note that I hardly, if ever, watch television. My ADD has a hard time putting up with 20 minutes per hour of commercials for products I don&#x27;t want and shouldn&#x27;t buy. I do though enjoy an occasional movie courtesy of a friend&#x27;s &quot;borrowed&quot; Netflix streaming account. My roommate, however, watches football. He did, I should say, until a certain Sunday last fall. I walked in the door that day and began to take off my shoes when my roommate asked, &quot;What&#x27;s up with the TV? I was watching the game and it just turned off. The standby light isn&#x27;t even on.&quot; I walked over to look at it; the power supply was warmer than usual. I tried turning it on again. It obeyed for a few seconds and then promptly died again. &quot;It&#x27;s done.&quot; I said. &quot;I think the transformer burned out.&quot;</p>
  <p>I still have decent enough soldering skills that I could probably fix the transformer. I also thought about replacing the unit with something nicer. But then a more intriguing thought occurred to me, &quot;What if this could be a breakpoint.&quot; Like, I suspect, most kids growing up in the States in the nineties, I can&#x27;t remember not having access to a television. What an interesting experiment it would be to consciously decide to not have TV available. My goal, aside from curiosity and a desire to not spend a few hundred dollars, was to make other, more beneficial means of entertainment more appealing. I would start with reading more and follow with writing more. This experiment combined with my interests lead me to William Zinsser&#x27;s phenomenal book, &quot;On Writing Well.&quot;</p>
  <p>I would highly recommend that anyone who ever plans to put at least three words into sequence should read this book. Not only is it relevant for long-form writing and essays but also public speaking and interoffice communication: two things I seem to do a lot of. What especially impacted me, though, was the final chapter. After just under 300 pages of discussing how to write well, he switches to a more philosophical subject. He begins to discuss <em>why</em> you should write well.</p>
  <blockquote>
  <p>He had a passion for quality and had no patience with the second rate; he never went into a store looking for a bargain. He charged more for his product because he made it with the best ingredients, and his company prospered…</p>
  </blockquote>
  <blockquote>
  <p>Only later did I realise that I took along on my journey another gift from my father: a bone-deep belief that quality is its own reward. I, too, have never gone into a store looking for a bargain. <a href="http://www.amazon.com/Writing-Well-30th-Anniversary-Nonfiction/dp/0060891548" title="On Writing Well, Seventh Edition, Revised and Updated. By William Zinsser. Pages 296-299.">#</a></p>
  </blockquote>
  <p>Ostensibly, those passages apply to writing. In reality, they apply to so much more. In reality, they represent a mitigation of so much of what&#x27;s wrong with modern anti-culture. As much as I enjoyed working as a consulting web developer, the consistent disinterest in quality drove me from it. I love writing good software. I love working on teams with people who <em>really</em> care about building something the right way. The way that will last. The way that will make our inevitable successors understand and agree with the choices and tradeoffs we made. The way that, when you have a long view, makes sense. My current team is a great example of this: 7 other people who love doing things right.</p>
  <p>Unfortunately this is not the prevailing trend. Not in the software industry or the automobile industry or the world. The prevailing attitude among so many people isn&#x27;t, &quot;I&#x27;d like to buy a really nice pair of shoes, take proper care of them, and wear them for the next 40 years.&quot; Instead people are raised to always want a new pair of shoes, a new house, a new car. Advertising indoctrinates them to believe that&#x27;s ok. &quot;You deserve a new pair of shoes&quot;, they hear, &quot;and it&#x27;s ok, they&#x27;re mass produced and they&#x27;re so cheap that everyone can afford them!&quot; And so, instead of buying 2 pair of Allen Edmunds or Aldens, they buy 20 pairs of Nike and New Balance.</p>
  <p>Not only is this wasteful and materialistic, but it robs one of the inherent joy that comes from good. Good is replaced by the cheap imitation that is new. Shiny. Gaudy. This isn&#x27;t anymore sustainable in shoes than it is in software. As programmers and system architects, it&#x27;s our job to emphasis this. Capitalism is capitalism. It will always prefer cheap to good and now to later. The only way to prevent that is to stop undercutting ourselves and stop minimising the costs that come with doing something wrong.</p>
  <blockquote>
  <p>Sure, the application could be done in a month by an understaffed development team. It&#x27;s going to be ugly, have a bad user experience, tarnish the company&#x27;s name, and occasionally delete the user&#x27;s entire home folder. Or…we could hire another developer, take 3 months to build it, and make record profits.</p>
  </blockquote>
  <p>Some companies will never accept that. A bad application is good enough, they will say. Those companies will eventually fail. As a developer, though, it&#x27;s still our job to try. &quot;Several magazine editors have told me I&#x27;m the only writer they know who cares what happens to his piece after he gets paid for it…yet to defend what you&#x27;ve written is a sign that you are alive.&quot;<a href="http://www.amazon.com/Writing-Well-30th-Anniversary-Nonfiction/dp/0060891548" title="On Writing Well, Seventh Edition, Revised and Updated. By William Zinsser. Pages 296-299.">#</a> To defend quality is a sign that you&#x27;re a good programmer. To defend quality is to have satisfaction is your work.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[SilverStripe 2.x Templates]]></title>
        <description><![CDATA[SilverStripe and a few other frameworks use a variant of standard HTML comments within their template engines. This enables the engine to easily strip out comments before sending the page to the client, but it does cause an issue for most syntax highlighting text editors. Since Sublime Text 2 is my editor of choice, heres a simple Gist to make it syntax highlight these comments correctly. Add the above excerpt to HTML/HTML.tmLanguage in your Sublime Text 2 Packages directory.]]></description>
        <link>https://crgwbr.com/silverstripe-2-x-templates/</link>
        <guid isPermaLink="false">1566c5e4-e391-44ef-a5c7-4bb41e7b4763</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 02 Oct 2012 20:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<pre><code>&lt;!-- Add this block to HTML/HTML.tmLanguage --&gt;
  &lt;dict&gt;
     &lt;key&gt;begin&lt;/key&gt;
     &lt;string&gt;&amp;lt;%--&lt;/string&gt;
     &lt;key&gt;captures&lt;/key&gt;
     &lt;dict&gt;
        &lt;key&gt;0&lt;/key&gt;
        &lt;dict&gt;
           &lt;key&gt;name&lt;/key&gt;
           &lt;string&gt;punctuation.definition.comment.html&lt;/string&gt;
        &lt;/dict&gt;
     &lt;/dict&gt;
     &lt;key&gt;end&lt;/key&gt;
     &lt;string&gt;--%&amp;gt;&lt;/string&gt;
     &lt;key&gt;name&lt;/key&gt;
     &lt;string&gt;comment.block.html&lt;/string&gt;
     &lt;key&gt;patterns&lt;/key&gt;
     &lt;array&gt;
        &lt;dict&gt;
           &lt;key&gt;match&lt;/key&gt;
           &lt;string&gt;--&lt;/string&gt;
           &lt;key&gt;name&lt;/key&gt;
           &lt;string&gt;invalid.illegal.bad-comments-or-CDATA.html&lt;/string&gt;
        &lt;/dict&gt;
        &lt;dict&gt;
           &lt;key&gt;match&lt;/key&gt;
           &lt;string&gt;%&gt;&lt;/string&gt;
           &lt;key&gt;name&lt;/key&gt;
           &lt;string&gt;invalid.illegal.bad-comments-or-CDATA.html&lt;/string&gt;
        &lt;/dict&gt;
        &lt;dict&gt;
           &lt;key&gt;include&lt;/key&gt;
           &lt;string&gt;#embedded-code&lt;/string&gt;
        &lt;/dict&gt;
     &lt;/array&gt;
  &lt;/dict&gt;</code></pre>
  <p>SilverStripe and a few other frameworks use a variant of standard HTML comments within their template engines. This enables the engine to easily strip out comments before sending the page to the client, but it does cause an issue for most syntax highlighting text editors. Since Sublime Text 2 is my editor of choice, heres a simple Gist to make it syntax highlight these comments correctly. Add the above excerpt to <code>HTML/HTML.tmLanguage</code> in your Sublime Text 2 Packages directory.</p>
  <p><a href="https://gist.github.com/3953001">View with Gist</a></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Purge]]></title>
        <description><![CDATA[I don't have enough time. More specifically, I've decided I don't have enough time. Everyday I get up at at 6:00AM with high hopes for the day: optimistic views on what I'll accomplish; which tickets I'll finally polish off; how much closer to the next milestone my team will be. Yet everyday, by the time I'm too exhausted to think about anything worthwhile, I'm disappointed. The issues I wanted to fix: still open; the random tasks requested of me throughout the day: still unchecked to-do items. It's been this way for a while and I've had enough.]]></description>
        <link>https://crgwbr.com/purge/</link>
        <guid isPermaLink="false">f86fc04a-adba-4509-b844-f35c4624e5d0</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Thu, 20 Sep 2012 20:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>I don&#x27;t have enough time. More specifically, I&#x27;ve <em>decided</em> I don&#x27;t have enough time. Everyday I get up at at 6:00AM with high hopes for the day: optimistic views on what I&#x27;ll accomplish; which tickets I&#x27;ll finally polish off; how much closer to the next milestone my team will be. Yet everyday, by the time I&#x27;m too exhausted to think about anything worthwhile, I&#x27;m disappointed. The issues I wanted to fix: still open; the random tasks requested of me throughout the day: still unchecked to-do items. It&#x27;s been this way for a while and I&#x27;ve had enough.</p>
  <p>Don&#x27;t misunderstand what I&#x27;m saying: I love my job and I love the people I work with. Like any programmer these days, recruiters contact me semi-frequently, yet they never receive anything more than &quot;Thanks, but I&#x27;m content,&quot; from me. Besides, I love being busy. Like most other ADD inflicted introverts, there are few things I fear more than not being busy. Being busy with meaningful tasks gives someone&#x27;s life purpose, direction, and satisfaction. So what&#x27;s the issue?</p>
  <p>The issue is that my backlog continues to grow and is quickly exceeding my capacity. I can keep up at work—but that&#x27;s far from all I need to do in a day. Regularly, I&#x27;ll decide I need to read a new book or I&#x27;ll come up with a programming idea I&#x27;d like to try. Someone recently gave me a guitar, so now I need to learn how to play it. I have sticky notes everywhere reminding me to take more time to write, to go cycling for a few hours, to meditate, to learn to speak Mandarin. All of these are important tasks with specific (although not always obvious) reasons for their existence; all of them sitting in my backlog waiting to get done. At my current burn-rate that day will never come.</p>
  <p>Something needs to change. But what? I can&#x27;t work less—In all reality I should work more. I can&#x27;t eliminate any of the projects above. But I can eliminate kipple. How much time do I spend on email every day? What about Twitter? Instagram? Facebook? RSS Feeds? Honestly, I don&#x27;t know. I have absolutely no empirical data that says I&#x27;m wasting my time and cognitive energy reading Twitter, but I know it&#x27;s taking a non-trivial chunk of time. Time that could be better spent finally reading the untouched books sitting on my shelf, becoming more physically fit, meditating and regaining perspective, or spending time with the people I care about.</p>
  <p>I&#x27;ve decided to take action. I&#x27;m not getting rid of Twitter or Facebook or Reeder, but I&#x27;m cutting them off at the knees. I just cut my Twitter following count by 60%. I cut Facebook down to 30 friends that I really, actually know. In Reeder I now subscribe to 36 RSS feeds, down from a peak of over 150 a few years ago.</p>
  <p>I&#x27;m keeping all of these services around because they do have value: they help me be aware of important news and keep in touch with good friends. I&#x27;ve simply trimmed the fat and raised the bar on the quality of the content I&#x27;m going to consume. Too much of the content I was spending time on was completely inconsequential and frighteningly shallow. Instead of content being a way to convey important information, it was a way to spend time and to distract myself from thinking about difficult problems. That&#x27;s something I can no longer afford to do.</p>
  <p>In short: time is the most valuable and exceedingly scarce resource in the Universe, so I&#x27;ve decided to treat it as such.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Skyscraper of Bananas]]></title>
        <description><![CDATA[When you sit back and look at it web application development is a total disaster. If you tried to conceive of the "ideal" UI platform (or even a "good" one) you would never, in a million years, come up with what we've got now. It is a testament to the skills of everyone in the industry that things like google maps, Gmail etc. can be built on this sky-scraper of bananas. No wonder it is hard to learn.— josephcooney]]></description>
        <link>https://crgwbr.com/skyscraper-of-bananas/</link>
        <guid isPermaLink="false">30a8311f-d10a-4c2c-995e-706fdb220487</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 03 Jul 2012 20:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<blockquote>
  <p>When you sit back and look at it web application development is a total disaster. If you tried to
  conceive of the &quot;ideal&quot; UI platform (or even a &quot;good&quot; one) you would never, in a million years,
  come up with what we&#x27;ve got now. It is a testament to the skills of everyone in the industry that
  things like google maps, Gmail etc. can be built on this sky-scraper of bananas. No wonder it is
  hard to learn.—<a href="http://news.ycombinator.com/item?id=4579008">josephcooney</a></p>
  </blockquote>
  <p>I completely agree, yet despite being a skyscraper of bananas with a million moving parts, a well designed web app still has both better typography and a better user experience than the vast majority of desktop software—especially desktop software on Windows. What a great example of how tools matter a lot less than most people think. What really matters in building a great product isn&#x27;t the language it&#x27;s written in, but rather hard work and great attention to detail.</p>
  <p><a href="http://news.ycombinator.com/item?id=4578484">Hacker New—Why Codecademy is overrated and missing it&#x27;s target audience</a></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Good Design Is…]]></title>
        <description><![CDATA[This couch doesn't have drop shadows and an unnecessary 'loading' animation, metaphorically speaking. This article is a great description of the product of what good designers do. It's cheap to manufacture. Easily deployed and assembled. Well suited to its primary purpose. Incorporates multiple additional uses without compromising the primary use. Flexible enough to be reconfigured easily for different locations. Best of all, it doesn't have any unnecessary 'design' frills. — HN Comment dicussing Ikea furnature]]></description>
        <link>https://crgwbr.com/good-design-is/</link>
        <guid isPermaLink="false">ff5f2fc3-f0cd-45e0-8bb5-8e3754105f30</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Mon, 07 May 2012 20:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<blockquote>
  <p>This couch doesn&#x27;t have drop shadows and an unnecessary
  &#x27;loading&#x27; animation, metaphorically speaking. This article
  is a great description of the product of what good designers
  do. It&#x27;s cheap to manufacture. Easily deployed and assembled.
  Well suited to its primary purpose. Incorporates multiple
  additional uses without compromising the primary use. Flexible
  enough to be reconfigured easily for different locations. Best
  of all, it doesn&#x27;t have any unnecessary &#x27;design&#x27;
  frills. — <a href="http://news.ycombinator.com/item?id=4036753">HN Comment dicussing Ikea furnature</a></p>
  </blockquote>
  <p>In otherwords, good design is discovering what you think is a new use case for a product and then realizing, &quot;the designers obviously already thought about this.&quot; Good design is the complete lack of arbitrary decisions.</p>
  <p><a href="http://www.lengrand.fr/2012/05/design-innovation-and-hacking-in-a-couch/">Design, Innovation and Hacking in a couch</a></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[The Difference Between Online and Print]]></title>
        <description><![CDATA[I really hate that people think writing online needs to be significantly dumbed down and shorter than writing in print. Studies have shown that people are conditioned to skip around and to read only small blocks of text when reading online, but I hardly think that's the fault of the LCD they're looking at. When you compare the two mediums, print and web, I don't think the brain knows the difference between looking at letters on a computer or letters on a sheet of dead tree. LCD's do arguably cause more eye-strain, but that's slowly improving with technologies like e-ink.]]></description>
        <link>https://crgwbr.com/the-difference-between-online-and-print/</link>
        <guid isPermaLink="false">7c476260-a964-410e-98c9-1fc97b663e71</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Mon, 19 Mar 2012 20:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>I really hate that people think writing online needs to be significantly dumbed down and shorter than writing in print. Studies have shown that people are conditioned to skip around and to read only small blocks of text when reading online, but I hardly think that&#x27;s the fault of the LCD they&#x27;re looking at. When you compare the two mediums, print and web, I don&#x27;t think the brain knows the difference between looking at letters on a computer or letters on a sheet of dead tree. LCD&#x27;s do arguably cause more eye-strain, but that&#x27;s slowly improving with technologies like e-ink.</p>
  <p>The difference, I think, is our own fault. When&#x27;s the last time you saw an online article that looked like page from The Hitchhiker&#x27;s Guide to the Galaxy or Anathem? Don&#x27;t be surprised if you can&#x27;t think of one, because I can&#x27;t either. Online reading is plagued by absolutely horribly designed flash ads and other sorts of chrome surrounding it. We&#x27;re programmed to be drawn to motion, so if there&#x27;s an invasive flash ad complete with video and animation sitting right next to a long-form article, who can blame our brain for tending to stare at it rather than focus on what we&#x27;re reading?</p>
  <p>The solution then, to online writing, is not to write ever shorter articles. Who wants to live in a world where the New York Times produces nothing but Twitter posts? Rather I think we need to bring back the long-form article and remove what has been slowly killing it: distractions, bad typography and noisy pages. Then, just maybe, we&#x27;ll be able to regain the attention of the continually more distracted reader.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Node.js isn't a silver bullet, but it's still a bullet.]]></title>
        <description><![CDATA[This morning, on a conference mailing list, I made some disparaging remarks about Node.js (the title of this post, in fact). A couple people asked me why I felt that way. Rather than respond individually, I'll just list my reasons here — codeslinger.posterous.com]]></description>
        <link>https://crgwbr.com/node-js-isnt-a-silver-bullet-but-its-still-a-bullet/</link>
        <guid isPermaLink="false">007efa71-0370-4b6b-a94c-c71541a65e8f</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Wed, 01 Feb 2012 22:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<blockquote>
  <h6><a href="http://codeslinger.posterous.com/if-youre-using-nodejs-youre-doing-life-wrong">If you&#x27;re using Node.js, you&#x27;re doing life wrong</a></h6>
  <p>This morning, on a conference mailing list, I made some disparaging remarks about Node.js
  (the title of this post, in fact). A couple people asked me why I felt that way. Rather than respond
  individually, I&#x27;ll just list my reasons here — <a href="http://codeslinger.posterous.com/if-youre-using-nodejs-youre-doing-life-wrong">codeslinger.posterous.com</a></p>
  </blockquote>
  <p>I really don&#x27;t understand all the hate for Node.js. It seems like many of the articles and rants against Node.js assume that Node.js was supposed to be good at everything. It&#x27;s not. Just like every other framework and language in existence, it&#x27;s good at some things and bad at others.</p>
  <p>Node.js, at least from my understanding, was designed to be great at transporting small bits of information around the internet very quickly, and in real-time. Server-side events, instant messaging apps, real-time games, and collaboration tools are all great example of this. Take for example <a href="https://trello.com/">Trello</a>. Trello is a real-time collaboration app that leverages Socket.io and Node.js to enable real-time propagation of events and state-changes between clients. You could do the same thing with long-polling ajax or even frequent polling, but those both come with the cost of tying up unnecessary worker threads on the server and dealing with extra requests. Node.js on the other hand is inherently <em>great</em> at this. It&#x27;s asynchronous event based architecture makes receiving, processing, and sending real-time events simple, painless, and very fast.</p>
  <p>At the same time, Node.js isn&#x27;t isn&#x27;t especially good at computation. If for example you were trying to build an API to return the nth number in the Fibonacci sequence, Node.js would almost certainly be a bad choice? Why? The whole reason to use Node.js is based around the idea of not waiting on things. Instead of waiting for a db query to return results, it just triggers a db query and sets a callback event. Then, while the query is processing, your program can be doing other things (like handling another request). This is what makes Node.js seem so fast, without actually using more than 1 CPU core. In our example of computing Fibonacci numbers, however, the program doesn&#x27;t need to wait on <em>anything</em>. The speed at which such an API can return results is directly linked to how fast it can compute a result. So here it would be better to use another, computationally faster language like Haskell or Scala.</p>
  <p>So what&#x27;s the point of all this? The point is that it&#x27;s silly and irrational to complain that a framework isn&#x27;t good at completing task A, when it was only ever designed to do task B. Node.js is extraordinarily good at what it was designed to do- so don&#x27;t rant that it&#x27;s bad at something else.</p>
  <p>You can discuss this post on <a href="http://news.ycombinator.com/item?id=3548369">Hacker News</a>.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Becoming Great]]></title>
        <description><![CDATA[No matter how hard some people try and find it, there is no secret to being a great programmer. The same goes for any trade, whether you're a designer, painter, writer, or carpenter. The only "secret" is to be passionate and fully present at whatever it is you're doing, to work at it day and night, and to keep doing that for a really, really long time. No one has ever become a great writer by buying the perfect distraction free writing environment. No one has ever become a master programmer by trying to convince the internet that only node.js and mongodb are webscale. If more people realized that, many more people would be a lot closer to actually being masters.]]></description>
        <link>https://crgwbr.com/becoming-great/</link>
        <guid isPermaLink="false">477d34e3-4c76-4c78-b1ef-3a1ac984694b</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Thu, 05 Jan 2012 22:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>No matter how hard some people try and find it, there is no secret to being a great programmer. The same goes for any trade, whether you&#x27;re a designer, painter, writer, or carpenter. The only &quot;secret&quot; is to be passionate and fully present at whatever it is you&#x27;re doing, to work at it day and night, and to keep doing that for a really, really long time. No one has ever become a great writer by buying the perfect distraction free writing environment. No one has ever become a master programmer by trying to convince the internet that only node.js and mongodb are webscale. If more people realized that, many more people would be a lot closer to actually being masters.</p>
  <p><a href="http://vimeo.com/7192517">Makebelieve Help, Old Butchers, and Figuring Out Who You Are (For Now)</a></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Stop Being Comfortable]]></title>
        <description><![CDATA[Until recently, when I moved to Brooklyn, I lived in a small town on the outskirts of the Rust Belt region of the US. Despite having the cognitive bias that all humans have, that makes us think the culture we grow up in is “standard,” I had traveled enough to know something was generally amiss with the attitude of the area. Not necessarily consciously mind you, but I did have that feeling.]]></description>
        <link>https://crgwbr.com/stop-being-comfortable/</link>
        <guid isPermaLink="false">5e1cb777-7ecb-4bca-a36e-bf4d96719837</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Wed, 28 Dec 2011 22:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>Until recently, when I moved to Brooklyn, I lived in a small town on the outskirts of the <a href="http://en.wikipedia.org/wiki/Rust%5FBelt">Rust Belt</a> region of the US. Despite having the cognitive bias that all humans have, that makes us think the culture we grow up in is “standard,” I had traveled enough to know something was generally amiss with the attitude of the area. Not necessarily consciously mind you, but I did have that feeling.</p>
  <p>After moving to NY however, I&#x27;ve been able to more fully identify what bothered me about the pervasive culture of that area: people were too comfortable. Not materially comfortable, but mentally comfortable.</p>
  <h5>comfort considered harmful?</h5>
  <p>Perhaps the anti-quality I&#x27;m describing could be better called complacent or ambitionless, but regardless of the technical symantics, I think the word comfortable describes it well. So what is it exactly that I&#x27;m describing? The almost subconscious feeling of hopelessness; that there&#x27;s nothing they can do about noticably subpar conditions. Instead of taking action and working towards making improvements, they decide that either 1) there&#x27;s nothing they can do about the problem, 2) someone else will fix it for them, 3) the problem will magically fix itself, or 4) they&#x27;d rather ignore the problem than go out of their comfort zone to try to fix it.</p>
  <p>All of those are fairly major problems, but I&#x27;m of course a programmer and not a sociologist, so I only feel qualified to talk about the last one (as it often affects programmers). All too often programmers, including myself, get stuck in a comfort zone. They decide that language X on platform Y is their domain and anything else isn&#x27;t their problem. They get comfortable with one litle corner of their teams application, decide to become it&#x27;s &quot;owner,&quot; and that they shouldn&#x27;t stray to other parts of the application.</p>
  <p>&quot;Owning&quot; a piece of code isn&#x27;t the problem, in fact thats generally a very good thing; the problem is the fear it can induce. When you confine yourself to arbitrary bounds for very long, anything on the outside of those bounds suddenly becomes scary. You start becoming afraid to touch anything else. Afraid to write/rewrite something unfamiliar simply because it&#x27;s outside your comfort zone. Absolutely afraid of traversing the stack. Terrified of <a href="http://www.codinghorror.com/blog/2004/11/dont-be-afraid-to-break-stuff.html">breaking stuff</a>.</p>
  <p>Confining yourself to working on code/problems/domains that you&#x27;re comfortable with will overtime cripple you as a programmer. So instead of getting comfortable and settling in, please, find something that looks like black magic and figure out how it works. Find a bug on a different OSI layer. Break stuff! Just whatever you do, don&#x27;t stay comfortable.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Supersaturated]]></title>
        <description><![CDATA[A supersaturated solution is one in which the saturation point, at which no more material will dissolve, has been exceeded. This can occur because the saturation point becomes higher as the temperature of the solution is increased. When you dissolve the material at a high temperature and then cool the solution, the material sometimes doesn't crystallize out because the molecules don't know how. They require something to get them started, a seed crystal, or a grain of dust or even a sudden scratch or tap on the surrounding glass.]]></description>
        <link>https://crgwbr.com/supersaturated/</link>
        <guid isPermaLink="false">fd08ec00-c700-4c13-bfb2-3741568721f8</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Mon, 26 Dec 2011 22:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<blockquote>
  <p>A supersaturated solution is one in which the saturation
  point, at which no more material will dissolve, has been
  exceeded. This can occur because the saturation point
  becomes higher as the temperature of the solution is
  increased. When you dissolve the material at a high
  temperature and then cool the solution, the material sometimes
  doesn&#x27;t crystallize out because the molecules don&#x27;t know how.
  They require something to get them started, a seed crystal, or
  a grain of dust or even a sudden scratch or tap on the
  surrounding glass.</p>
  <p>He walked to the water tap to cool the solution but never got
  there. Before his eyes, as he walked, he saw a star of
  crystalline material in the solution appear and then grow
  suddenly and radiantly until it filled the entire vessel. He
  saw it grow. Where before was only clear liquid there was now
  a mass so solid he could turn the vessel upside down and
  nothing would come out. The one sentence &quot;I hope you are
  teaching Quality to your students&quot; was said to him, and within
  a matter of a few months, growing so fast you could almost see
  it grow, came an enormous, intricate, highly structured mass
  of thought, formed as if by magic.</p>
  <p>— Pirsig, Robert M. (2009).
  Zen and the Art of Motorcycle Maintenance
  (Kindle Locations 2950-2959).
  HarperCollins e-books.
  Kindle Edition.</p>
  </blockquote>
  <p>What idea is supersaturating your mind? What will be the scratch in the glass that starts crystallization?</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[What I've been up to]]></title>
        <description><![CDATA[If you follow me on Twitter , you might have noticed that I've been fairly quiet about what I've been working on for the past several months. There's a few good reasons for this, but mainly it's just because of how busy I've actually been.]]></description>
        <link>https://crgwbr.com/what-ive-been-up-to/</link>
        <guid isPermaLink="false">6a10d47e-a4c3-46fa-a06b-1a06fc924684</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 06 Dec 2011 22:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>If you follow me on <a href="http://twitter.com/crgwbr">Twitter</a>, you might have noticed that I&#x27;ve been fairly quiet about what I&#x27;ve been working on for the past several months. There&#x27;s a few good reasons for this, but mainly it&#x27;s just because of how busy I&#x27;ve actually been.</p>
  <p>In June 2011, I got an invitation to move to Brooklyn, NY and work as a volunteer at the World Headquarters of Jehovah&#x27;s Witnesses for a period of 3 months. Since this is something I applied for a while ago and have been working towards for as long as I can remember, I gladly accepted. At the end of those 3 months I was asked to stay another 3 months, then recently I was asked to stay for another year. Despite having to turn down some great offers and opportunities from some very interesting companies, I&#x27;m quite happy that I decided to stay here.</p>
  <p>I&#x27;m working as a full-time developer on <a href="http://jw.org">jw.org</a>, a web-application with the purpose of distributing free, downloadable literature, and providing many other non-public facing, data-centric services. It&#x27;s definitely exciting to work on a project of such large scale, serving over a quarter million uniques per day.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Switch Statements in Python]]></title>
        <description><![CDATA[{<1>}]]></description>
        <link>https://crgwbr.com/switch-statements-in-python/</link>
        <guid isPermaLink="false">47440628-ae25-4015-8756-6ee52fc96fc8</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 31 Aug 2010 20:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>{&lt;1&gt;}<img src="pythonpowered.png" alt="Python" title="Python"/></p>
  <p>Anyone familiar with Java, C, C++, or another similar language would easily recognize this code:</p>
  <pre><code>switch (n) {
      case 0:
          puts(&quot;You typed zero.&quot;);
          break;
      case 9:
          puts(&quot;You typed nine.&quot;);
          break;
      default:
          puts(&quot;You didn’t type Zero or Nine.&quot;);
          break;
  }</code></pre>
  <p>This is a simple switch statement, generally used when IF-THEN-ELSE blocks would be overly complex or unwieldy. Python, however, doesn’t have switch { case } syntax, so we have to be a little clever. The most obvious way to duplicate the above code would be:</p>
  <pre><code>if n == 0:
      print &quot;You typed zero.&quot;
  elif n == 5:
      print &quot;You typed five.&quot;
  elif n == 9:
      print &quot;You typed nine.&quot;
  else:
      print &quot;You didn&#x27;t type zero or nine.&quot;
  </code></pre>
  <p>That’ll work, but its a little wasteful when it comes to line count or more complex cases. We can simplify this quite a bit by making use of Python dictionaries:</p>
  <pre><code>cases = {0 : &quot;You typed zero.&quot;,
           5 : &quot;You typed five.&quot;,
           9 : &quot;You typed nine.&quot;}
  if n in cases.keys():
      print cases[n]
  else:
      print &quot;You didn&#x27;t type zero or nine.&quot;
  </code></pre>
  <p>In that example, every possible case is placed as a dictionary key and the string to be printed is the value. In this example we only save 1 line of code, but we also cut the execution time drastically. On my machine (Core i7 920), example-1 takes 4.999ms to execute. Example-2, on the other hand takes only 1.999ms; this means its a speedup of roughly 250%. An extra 3ms may not be much, but in complex cases, limited hardware environments, or extremely time sensitive application it does.</p>
  <p>Finally, we have one more example. What if you want to call functions instead of print strings? Thats easily done with almost the same code:</p>
  <pre><code>cases = {0 : Object.function_1,
           5 : Object.function_2,
           9 : Object.function_3}
  if n in cases.keys():
      cases[n]()
  else:
      Object.default()
  </code></pre>
  <p>In this example, each function call (minus the calling parenthesis) is placed as the value of its respective key. Since we don’t include the parenthesis, the function isn’t called yet. Then, on line 5, we select the appropriate function and call it.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Let them eat cake?]]></title>
        <description><![CDATA[I’ve always had a feeling that Marie Antoinette’s famous words, “Let them eat cake,” didn’t mean what everyone else thought. Traditionally, the story goes that when she heard the French peasants were starving because of lack of bread, her response was “Let them eat cake.” Most people take this with a distinctly callous meaning, but according to Wikipedia it was actually quite an economic breakthrough.]]></description>
        <link>https://crgwbr.com/let-them-eat-cake/</link>
        <guid isPermaLink="false">c5755a5e-fab7-4e0d-ada0-fb5657ce32b5</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Tue, 03 Aug 2010 20:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>I’ve always had a feeling that Marie Antoinette’s famous words, “Let them eat cake,” didn’t mean what everyone else thought. Traditionally, the story goes that when she heard the French peasants were starving because of lack of bread, her response was “Let them eat cake.” Most people take this with a distinctly callous meaning, but according to Wikipedia it was actually quite an economic breakthrough.</p>
  <p><em>Marie Antoinette did not actually use the phrase &quot;let them eat cake&quot; when she heard that the French peasantry was starving due to a dearth of bread. The phrase was first published in Rousseau’s &quot;Confessions&quot; when Marie was only 10 years old and most scholars believe that Rousseau coined it himself, or that it was said by Maria-Theresa, the wife of Louis XIV. Furthermore, although the proclamation may sound callous, it was actually made out of generosity and economic logic. What Rousseau or Marie-Theresa actually said was, &quot;Qu’ils mangent de la brioche,&quot; essentially meaning that the peasants should be able to eat brioche, a sweeter egg-based bread instead of their normal fare. In fact, French law mandated that bakers sell their brioche at the same price as their cheap bread if their cheap bread ran out. Marie Antoinette was a very unpopular ruler and many people therefore attribute the phrase &quot;let them eat cake&quot; to her, in keeping with her reputation as being hard-hearted and disconnected with her subjects.</em></p>
  <p>So, what she actually meant was if a baker ran out of bread, they had to sell their more expensive ‘cake’ for the same price so that the peasants could afford it.</p>
  <p><a href="http://en.wikipedia.org/wiki/List%5Fof%5Fcommon%5Fmisconceptions#Europe">Wikipedia Article</a></p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Good Design]]></title>
        <description><![CDATA[I just finished watching the documentary Objectified for the second time. The entire documentary is great, but one of my favorite portions is the 10 rules of good design given by Dieter Rams:]]></description>
        <link>https://crgwbr.com/good-design/</link>
        <guid isPermaLink="false">2490fac4-2a93-40dc-95c5-9989c66e8ad2</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Wed, 19 May 2010 20:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>I just finished watching the documentary <em>Objectified</em> for the second time. The entire documentary is great, but one of my favorite portions is the 10 rules of good design given by Dieter Rams:</p>
  <ul>
  <li>Good design should be innovative.</li>
  <li>Good design should make a product useful</li>
  <li>Good design is aesthetic design</li>
  <li>Good design will make a product understandable</li>
  <li>Good design is honest</li>
  <li>Good design is unobtrusive</li>
  <li>Good design is long-lived</li>
  <li>Good design is consistent in every detail</li>
  <li>Good design is environmentally friendly</li>
  <li>Good design is as little design as possible</li>
  </ul>
  <p>Whether you’re a web, graphic, or industrial designer, we all get stuck in ruts occasionally: blindly following trends, designing for the sake of design, etc. I think its very beneficial to frequently stop and use these rules to evaluate our latest work. It may cause you to realize that something in your design is completely arbitrary and is either unneeded or can be improved.</p>]]></content:encoded>
      </item>
      <item>
        <title><![CDATA[Hello World]]></title>
        <description><![CDATA[Welcome to the personal blog of me, Craig Weber. I am a graphics/web designer, programmer, and student in Northwestern Pennsylvania. This blog acts as a résumé and log of my past and current experiances and projects, in an effort to demonstrate my skills to future employers.]]></description>
        <link>https://crgwbr.com/hello-world/</link>
        <guid isPermaLink="false">082cef89-4f65-4116-b25e-1440e855fb0a</guid>
        <dc:creator><![CDATA[Craig Weber]]></dc:creator>
        <pubDate>Thu, 01 Jan 2009 22:00:00 GMT</pubDate>
        <content:encoded><![CDATA[<p>Welcome to the personal blog of me, Craig Weber. I am a graphics/web designer, programmer, and student in Northwestern Pennsylvania. This blog acts as a résumé and log of my past and current experiances and projects, in an effort to demonstrate my skills to future employers.</p>]]></content:encoded>
      </item>
</channel>
</rss>
