<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dw="https://www.dreamwidth.org">
  <id>tag:dreamwidth.org,2012-03-19:1579314</id>
  <title>The Disparate Musings of Pixie</title>
  <subtitle>pinterface</subtitle>
  <author>
    <name>pinterface</name>
  </author>
  <link rel="alternate" type="text/html" href="https://pinterface.dreamwidth.org/"/>
  <link rel="self" type="text/xml" href="https://pinterface.dreamwidth.org/data/atom"/>
  <updated>2020-11-13T04:56:16Z</updated>
  <dw:journal username="pinterface" type="personal"/>
  <entry>
    <id>tag:dreamwidth.org,2012-03-19:1579314:5189</id>
    <link rel="alternate" type="text/html" href="https://pinterface.dreamwidth.org/5189.html"/>
    <link rel="self" type="text/xml" href="https://pinterface.dreamwidth.org/data/atom/?itemid=5189"/>
    <title>To The Interns I Couldn't Hire</title>
    <published>2020-11-13T04:56:16Z</published>
    <updated>2020-11-13T04:56:16Z</updated>
    <category term="work"/>
    <category term="programming"/>
    <category term="development"/>
    <dw:security>public</dw:security>
    <dw:reply-count>0</dw:reply-count>
    <content type="html">&lt;p&gt;For the first time in my career, I recently got to sit in the hiring chair.  My current employer let me look for a Software Engineering Intern.  (With a little luck, I'm hoping to repeat the process for a senior level developer in the near future.)&lt;/p&gt;

&lt;p&gt;I learned a lot, very quickly, doing that (a biggie: actually write down a list of questions to ask, don't just wing it—with my apologies to the very first person I interviewed, as I did not do a good job interviewing you), but this post is not about those lessons learned.  This post is for all the people I had to send a rejection letter to.  In my opinion, all of the people I interviewed were smart and capable of doing the job; actually picking one was a tough choice.&lt;/p&gt;

&lt;p&gt;While our chosen field of computer programming offers amazing opportunities, finding work can be difficult&lt;a href="#entry-20201112-fn-1" name="entry-20201112-ref-1"&gt;¹&lt;/a&gt;, so don't despair.  Here are some general tips to help you find work, and become a better developer.&lt;/p&gt;

&lt;p&gt;Caveat: These are all filtered through my path as a developer, which absolutely does not match yours.  The most obvious and major difference being I did not go to college.  Props on going for the degree!&lt;/p&gt;

&lt;h2&gt;Finding Work&lt;/h2&gt;

&lt;h3&gt;Give Talks&lt;/h3&gt;

&lt;p&gt;Local user groups—such as &lt;a href="https://www.dsmwebgeeks.com/"&gt;DSM Web Geeks&lt;/a&gt;, &lt;a href="https://www.pyowa.org/"&gt;Pyowa&lt;/a&gt;, &lt;a href="https://dsmjs.com/"&gt;DSMJS&lt;/a&gt;, &lt;a href="http://www.iowaruby.org/"&gt;Iowa Ruby Brigade&lt;/a&gt;, &lt;a href="https://www.cialug.org/"&gt;CIALUG&lt;/a&gt;, &lt;a href="http://dsmwebcollective.com/user-groups/"&gt;and so forth&lt;/a&gt; in the Des Moines area—are always desperate for talks, as are larger conferences such as &lt;a href="http://iowacodecamp.com/"&gt;Iowa Code Camp&lt;/a&gt;.  Too many people are terrified of speaking, thinking they need to be an expert, but that's just not true.  The perspective you bring as a newbie, showing us all something you've learned, is both incredibly valuable, and woefully under-represented.  It's also an effective way to catch the eye of a hiring manager or recruiter.&lt;/p&gt;

&lt;p&gt;Giving talks will help you become a better communicator, which is a vitally important part of being on a team.&lt;/p&gt;

&lt;h3&gt;Contribute to Open Source&lt;/h3&gt;

&lt;p&gt;I learned a &lt;em&gt;ton&lt;/em&gt; in my early days by contributing to open source projects.  If you can find a project you care about, with a community of smart, patient people willing to help guide you, contributing to existing projects will help make you a better programmer, and give you things you can point at to say: I did that.&lt;/p&gt;

&lt;p&gt;Contributing to open source will help you get better at revision control (e.g., git), reading code, and accepting feedback.&lt;/p&gt;

&lt;h3&gt;Develop Your Network&lt;/h3&gt;

&lt;p&gt;Getting your foot in the door of a place is a lot easier if you already know somebody on the inside.  Be friendly, hobnob with other developers (e.g., at local dev groups), and stay in touch with people.&lt;/p&gt;

&lt;p&gt;I've spent large chunks of my career as a lone developer and am a natural shut-in, so this is one area I regularly fail at.&lt;/p&gt;

&lt;h2&gt;Expand Your Mind&lt;/h2&gt;

&lt;p&gt;While learning the Framework Du Jour or a specific technology will help you be more employable in the short term, a solid foundation of knowledge will help you throughout your career, enabling you to pick up framework and language specifics more quickly, and to see the similarities with things which have come before.&lt;/p&gt;

&lt;p&gt;While it'd be difficult to point you to the books that helped shape me as an early developer—in part because books like "QBasic For Dummies" and Laura Lemay's "Teach Yourself HTML 4 in 21 Days" would be a tad anachronistic at this point (though it appears there is a &lt;a href="https://www.amazon.com/dp/0672336235"&gt;more modern version&lt;/a&gt; of the latter)—I can at least point you to some that have shaped me as a more intermediate developer.&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;a href="https://htdp.org/"&gt;How to Design Programs&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;This book is a gentle introduction to programming.  It's a solid look at programming starting from zero.  While you're almost certainly beyond it skill-wise already, I've been skimming through it lately and find the way it frames things can be helpfully clarifying.&lt;/dd&gt;
&lt;dt&gt;&lt;a href="http://groups.csail.mit.edu/mac/classes/6.001/abelson-sussman-lectures/"&gt;Structure and Interpretation of Computer Programs&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;This was the textbook for an MIT course of the same name for a long time, and takes you from basic functions to meta-circular evaluators.  Depending on what you've been exposed to already, it may well cover a gamut from "yeah, duh", to blowing your mind.  Also freely available online as a textbook, and video lectures.&lt;/dd&gt;
&lt;dt&gt;&lt;a href="http://www.gigamonkeys.com/book/"&gt;Practical Common Lisp&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;Common Lisp is a really fascinating language, and can open your mind to possibilities you likely haven't imagined.  It certainly did for me!  As just two examples: multiple dispatch and the condition system are among the things I sorely miss in other languages.  &lt;em&gt;Also&lt;/em&gt; available freely online.&lt;/dd&gt;
&lt;dt&gt;&lt;a href="http://www.codersatwork.com/"&gt;Coders at Work&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;This book is a series of interviews with influential developers.  It's an interesting foray into the minds and experiences of people who have ventured down the path of software development you are just now beginning to embark upon.  This one isn't freely available, but it &lt;em&gt;is&lt;/em&gt; a fascinating read, in my opinion.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Admittedly, that list reflects a heavy Lisp heritage.  Lisp has had a heavy impact on me and the way I think about programs, but many languages can fall under the category of "mind-expanding".  You're likely already familiar with Java, which covers the space of modern, standard object-oriented design.  Some other design spaces you may want to explore, if you haven't already:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A purely functional language.  Purely functional code is often easier to understand and test than OO code, and you'll see its influence across a broad spectrum of languages.  A good way to explore this space is through &lt;a href="http://learnyouahaskell.com/"&gt;Haskell&lt;/a&gt;, which is a very mathy language—you could easily get lost in monads and category theory—, if you're into that sort of thing.&lt;/li&gt;
&lt;li&gt;Different concurrency models.  Erlang's actor-based concurrency model is fascinating, and often much better suited to problem domains than thinking about lower-level things like threads and mutexes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sadly, none of these things are likely to make you more employable on paper—a sad reality of how we evaluate developers today—but they will make you better at the job itself.&lt;/p&gt;

&lt;h2&gt;Be Well-Rounded&lt;/h2&gt;

&lt;p&gt;It took me entirely too long to learn, but there is more to life than slinging code, so don't forget to do things that aren't programming.  Take an improv class, learn some ballroom dancing, learn about user experience (UX).  Whatever interests you.&lt;/p&gt;

&lt;ol class="footnotes"&gt;
&lt;li&gt;&lt;a name="entry-20201112-fn-1" href="#entry-20201112-ref-1"&gt;⌃&lt;/a&gt;I am amazingly terrible at interviews, to the point where I've spent years in between jobs, and the jobs I've gotten are often in spite of my interview.  I &lt;em&gt;totally&lt;/em&gt; get how frustrating it is to be rejected.&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="https://www.dreamwidth.org/tools/commentcount?user=pinterface&amp;ditemid=5189" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/&gt; comments</content>
  </entry>
  <entry>
    <id>tag:dreamwidth.org,2012-03-19:1579314:3162</id>
    <link rel="alternate" type="text/html" href="https://pinterface.dreamwidth.org/3162.html"/>
    <link rel="self" type="text/xml" href="https://pinterface.dreamwidth.org/data/atom/?itemid=3162"/>
    <title>Clojure Is For Me, Go Is For My Team</title>
    <published>2015-05-07T05:56:25Z</published>
    <updated>2015-05-07T05:56:25Z</updated>
    <category term="go"/>
    <category term="lisp"/>
    <category term="work"/>
    <category term="clojure"/>
    <category term="programming"/>
    <category term="php"/>
    <dw:mood>contemplative</dw:mood>
    <dw:security>public</dw:security>
    <dw:reply-count>0</dw:reply-count>
    <content type="html">&lt;ul&gt;
&lt;li&gt;&lt;a href="#clojure-go-1"&gt;The Basic Problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#clojure-go-2"&gt;The Method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#clojure-go-3"&gt;Inheritance Is&amp;#x2026; Uh&amp;#x2026; Missing?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#clojure-go-4"&gt;Clojure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#clojure-go-5"&gt;Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#clojure-go-6"&gt;Subjective Comparisons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#clojure-go-7"&gt;Recommendation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#clojure-go-8"&gt;Available For Work&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Before my now-previous employer folded, we were being courted by a buyer (boy,
was that time period an emotional roller-coaster!).  This would have changed our
development direction somewhat, and so at the behest of my boss I spent a couple
of weeks exploring language options for our upcoming greenfield projects.
&lt;/p&gt;

&lt;p&gt;
I was asked to explore Clojure and Go, and compare them to our existing
language, PHP.  I decided to evaluate the languages by porting a fairly
stand-alone portion of our existing app into both Go and Clojure, and comparing
it to the PHP version.
&lt;/p&gt;

&lt;h3&gt;&lt;a name="clojure-go-1"&gt;&lt;/a&gt;The Basic Problem&lt;/h3&gt;
&lt;p&gt;
That relatively stand-alone portion of our app was our recipe importer.  It
would allow users to import recipes from external sites, for use within our
system.
&lt;/p&gt;

&lt;p&gt;
There are a number of standards for recipe formats: the &lt;a href="http://microformats.org/wiki/hrecipe"&gt;hRecipe microformat&lt;/a&gt;,
&lt;a href="http://schema.org/Recipe"&gt;schema.org's Recipe format&lt;/a&gt;, &lt;a href="http://rdf.data-vocabulary.org/rdf.xml"&gt;RDF&lt;/a&gt;, &lt;a href="http://data-vocabulary.org/Recipe"&gt;data-vocabulary.org's Recipe format&lt;/a&gt;, and
&lt;a href="https://xkcd.com/927/"&gt;probably a few others&lt;/a&gt;.  In addition, most of the sites we explicitly supported
either fail to mark up the entire recipe, interpreted the spec they purported to
follow in strange ways, or merely referenced a recipe from another site,
requiring us to follow the link chain to a parsable source.
&lt;/p&gt;

&lt;p&gt;
In other words: it's a real (non-toy) problem, it's sufficiently complicated to
get a reasonable feel for a language, and it's simple enough it can be ported
within a relatively short timeframe.
&lt;/p&gt;

&lt;h3&gt;&lt;a name="clojure-go-2"&gt;&lt;/a&gt;The Method&lt;/h3&gt;
&lt;p&gt;
I spent a week in each of Clojure and Go.  This was not enough time for a full
feature-parity port with the PHP version, but sufficiently long to present most
of the complexity of the problem (inheritance, parser registration, and so
forth).
&lt;/p&gt;

&lt;h3&gt;&lt;a name="clojure-go-3"&gt;&lt;/a&gt;Inheritance Is&amp;#x2026; Uh&amp;#x2026; Missing?&lt;/h3&gt;
&lt;p&gt;
Neither Go nor Clojure support "classical" OO inheritance.  It may be my brain
is broken through years of shoehorning things into inheritance hierarchies, but
it seems to me this particular problem happens to be a pretty good fit for
classic OO.  "Site X uses format Y, with these overrides."
&lt;/p&gt;

&lt;p&gt;
So the biggest stumbling block in both languages was simply coming up with a way
to model the problem.  To be perfectly honest, the solution I ended up in both
languages looks pretty much like classic OO shoehorned into whatever the
language &lt;i&gt;does&lt;/i&gt; support (&lt;a href="http://golang.org/doc/effective_go.html#embedding"&gt;embedded structs in Go&lt;/a&gt;, &lt;a href="http://clojure.org/multimethods"&gt;multimethods in Clojure&lt;/a&gt;), and
would almost certainly not be considered idiomatic by people with more
experience in each language.  (But then, &lt;a href="http://pinterface.dreamwidth.org/2408.html"&gt;neither would my PHP&lt;/a&gt;, and I've been
doing that for a very long time.)
&lt;/p&gt;

&lt;h3&gt;&lt;a name="clojure-go-4"&gt;&lt;/a&gt;Clojure&lt;/h3&gt;
&lt;p&gt;
As a lisper bent on proving that a lisp was a viable option, I started with
Clojure.
&lt;/p&gt;

&lt;p&gt;
I spent approximately three days figuring out how to even model the problem
without classic OO inheritance, and the following two building three generic
parsers and two site-specific ones.
&lt;/p&gt;

&lt;p&gt;
It was one of the most enjoyable weeks of my tenure.  Clojure is as flexible as
you'd expect a Lisp to be, the code was compact, reasonably fast, and I was
reminded of how much I'd been missing interactive development.
&lt;/p&gt;

&lt;p&gt;
When I presented it alongside the PHP it was a port of, reactions were mixed.
Some developers remarked that they had a very difficult time understanding it,
and that it would likely prevent the developers who worked on other codebases
(e.g., our mobile developers) from swooping in to make a quick fix.  My boss,
who had apparently spent a significant amount of time attempting to do something
useful in Clojure without much success, remarked that the comparison made
Clojure much easier to understand for him than it had been previously.
&lt;/p&gt;

&lt;h3&gt;&lt;a name="clojure-go-5"&gt;&lt;/a&gt;Go&lt;/h3&gt;
&lt;p&gt;
Next I tried Go.  Having already faced a lack of inheritance, and presented with
Go's much smaller field of options to choose from, I was able to jump right in,
and hit parity with the Clojure version in short order.
&lt;/p&gt;

&lt;p&gt;
I did not particularly enjoy programming in Go.  While Go is strongly
opinionated in surprisingly good ways, the edit, compile, debug cycle is
obnoxious and slow (&lt;a href="https://github.com/dshills/goauto"&gt;goauto&lt;/a&gt; may have helped reduce this pain), the code ends up
verbose and repetitive, and there are little to no facilities for abstracting
out patterns.
&lt;/p&gt;

&lt;p&gt;
The Go code received less objection when presented.  The lines were
significantly longer than either the PHP or Clojure versions, and vertically it
was very slightly less dense than the PHP version due to all the error checks.
&lt;/p&gt;

&lt;h3&gt;&lt;a name="clojure-go-6"&gt;&lt;/a&gt;Subjective Comparisons&lt;/h3&gt;
&lt;dl class="org-dl"&gt;
&lt;dt&gt; Code Feel &lt;/dt&gt;&lt;dd&gt;&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;

&lt;colgroup&gt;
&lt;col class="left" /&gt;
&lt;col class="left" /&gt;
&lt;col class="left" /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope="col" class="left"&gt;PHP&lt;/th&gt;
&lt;th scope="col" class="left"&gt;Clojure&lt;/th&gt;
&lt;th scope="col" class="left"&gt;Go&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class="left"&gt;&lt;img src="http://web.kepibu.org/log/2015/clojure-go/php-feel.png"&gt;&lt;/td&gt;
&lt;td class="left"&gt;&lt;img src="http://web.kepibu.org/log/2015/clojure-go/clojure-feel.png"&gt;&lt;/td&gt;
&lt;td class="left"&gt;&lt;img src="http://web.kepibu.org/log/2015/clojure-go/go-feel.png"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;
Is it any wonder I prefer lisps?  So much meatier.
&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt; Learning Curve / Programmer Effectiveness &lt;/dt&gt;&lt;dd&gt;   &lt;img src="http://web.kepibu.org/log/2015/clojure-go/time-effectiveness-graph.png" style="display: block; margin: 0.25em auto;"&gt;
Go was the easiest to pick up, but there isn't really anywhere left to go
once you do.  Clojure is definitely harder to become proficient with, but you
can leverage that into increased programmer efficiency later.  PHP has so
many pitfalls that anyone starting out is likely to end up negatively
productive at first, writing a horribly insecure buggy mess.
&lt;/dd&gt;
&lt;dt&gt; Developer Emotions &lt;/dt&gt;&lt;dd&gt;&lt;img src="http://web.kepibu.org/log/2015/clojure-go/happiness.png" style="display: block; margin: 0.25em auto;"&gt;
Clojure inspired a mixture of enthusiasm and curiosity, with strong concerns
for the practicality of finding and training developers in a language which
is so different from mainstream ones.  Go had an enthusiastic champion in my
boss, but was largely met with ennuitic "meh"s by other developers (golang is
basically boring-by-design).  PHP was loved by none and hated by some, with
the only argument for it being "we've already got a bunch of code written in
PHP".
&lt;/dd&gt;
&lt;/dl&gt;

&lt;h3&gt;&lt;a name="clojure-go-7"&gt;&lt;/a&gt;Recommendation&lt;/h3&gt;
&lt;p&gt;
I liked Clojure.  It's fun to write in, it feels very productive, and the power
lisps provide to build your own abstractions is unparalleled.  But as varied as
the quality of code was in our PHP, providing room for even &lt;i&gt;more&lt;/i&gt; variability
is a hard case to argue.  While I would have loved to recommend Clojure, asking
a team of devs who think in terms of C++ to make the transition was more than I
could have sold.
&lt;/p&gt;

&lt;p&gt;
Go, on the other hand, solves a lot of problems that we had with PHP.  Stuff the
wrong type?  Go would have caught that.  Inconsistently-formatted code?  &lt;code&gt;gofmt&lt;/code&gt;
would have fixed that.  Bugs because PHP did something unexpected unless you
thoroughly read and retained every aspect of the documentation?  Go's house
is not built from bundles of thorns.
&lt;/p&gt;

&lt;p&gt;
So while Go may not be the language for me, it would have been the right call
for our team.
&lt;/p&gt;

&lt;h3&gt;&lt;a name="clojure-go-8"&gt;&lt;/a&gt;Available For Work&lt;/h3&gt;
&lt;p&gt;
As you may have guessed by the mention of my previous employer folding, I am
presently available for work.  Feel free to &lt;a href="mailto:gigs@kepibu.org"&gt;contact me&lt;/a&gt; if you have something you
think I may be interested in.
&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="https://www.dreamwidth.org/tools/commentcount?user=pinterface&amp;ditemid=3162" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/&gt; comments</content>
  </entry>
  <entry>
    <id>tag:dreamwidth.org,2012-03-19:1579314:2662</id>
    <link rel="alternate" type="text/html" href="https://pinterface.dreamwidth.org/2662.html"/>
    <link rel="self" type="text/xml" href="https://pinterface.dreamwidth.org/data/atom/?itemid=2662"/>
    <title>Typehint Conversion Caveat Explained</title>
    <published>2014-09-21T23:23:16Z</published>
    <updated>2014-09-21T23:23:16Z</updated>
    <category term="work"/>
    <category term="programming"/>
    <category term="php"/>
    <dw:security>public</dw:security>
    <dw:reply-count>0</dw:reply-count>
    <content type="html">&lt;p&gt;I was highly amused by the reactions to my previous post on the &lt;a href="http://www.reddit.com/r/programming/comments/2eluxw"&gt;programming&lt;/a&gt; and &lt;a href="http://www.reddit.com/r/PHP/comments/2emajn"&gt;php&lt;/a&gt; subreddits.  So I spent a little time to isolate the &lt;a href="http://pinterface.dreamwidth.org/2408.html#typehint-arg-conversion-caveat"&gt;typehint conversion optional argument issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s not me, &lt;a href="https://bugs.php.net/bug.php?id=48847"&gt;it&amp;#8217;s PHP&lt;/a&gt;.  And frankly, I don&amp;#8217;t understand the point of the backtrace going to the trouble of making things references if doing things with those references isn&amp;#8217;t even supported.&lt;/p&gt;

&lt;p&gt;That bug description isn&amp;#8217;t entirely accurate as to what&amp;#8217;s going on, however, as some test cases show.&lt;/p&gt;

&lt;pre class="src" lang="php"&gt;
&lt;span style="font-weight:bold;color:#666"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;f&lt;/span&gt;(&lt;span style="color:#0a8;font-weight:bold"&gt;int&lt;/span&gt; &lt;span style="color:#950"&gt;$a&lt;/span&gt;, &lt;span style="color:#0a8;font-weight:bold"&gt;int&lt;/span&gt; &lt;span style="color:#950"&gt;$b&lt;/span&gt; = &lt;span style="color:#069"&gt;null&lt;/span&gt;, &lt;span style="color:#0a8;font-weight:bold"&gt;int&lt;/span&gt; &lt;span style="color:#950"&gt;$c&lt;/span&gt; = &lt;span style="color:#069"&gt;null&lt;/span&gt;) {
  &lt;span style="color:#369;font-weight:bold"&gt;error_log&lt;/span&gt;(json_encode([&lt;span style="color:#950"&gt;$a&lt;/span&gt;, &lt;span style="color:#950"&gt;$b&lt;/span&gt;, &lt;span style="color:#950"&gt;$c&lt;/span&gt;]));
  &lt;span style="color:#369;font-weight:bold"&gt;error_log&lt;/span&gt;(json_encode(&lt;span style="color:#369;font-weight:bold"&gt;func_get_args&lt;/span&gt;()));
}

&lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;changeArg&lt;/span&gt;(&lt;span style="color:#950"&gt;$code&lt;/span&gt;, &lt;span style="color:#950"&gt;$str&lt;/span&gt;) {
  &lt;span style="color:#369;font-weight:bold"&gt;preg_match&lt;/span&gt;(&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;/^Argument (&lt;/span&gt;&lt;span style="color:#b0b"&gt;\\&lt;/span&gt;&lt;span style="color:#D20"&gt;d+) /&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span style="color:#950"&gt;$str&lt;/span&gt;, &lt;span style="color:#950"&gt;$m&lt;/span&gt;);
  &lt;span style="color:#950"&gt;$i&lt;/span&gt; = &lt;span style="color:#950"&gt;$m&lt;/span&gt;[&lt;span style="color:#00D"&gt;1&lt;/span&gt;]-&lt;span style="color:#00D"&gt;1&lt;/span&gt;;
  &lt;span style="color:#950"&gt;$bt&lt;/span&gt; = &lt;span style="color:#369;font-weight:bold"&gt;debug_backtrace&lt;/span&gt;(&lt;span style="color:#069"&gt;null&lt;/span&gt;, &lt;span style="color:#00D"&gt;2&lt;/span&gt;);
  ++&lt;span style="color:#950"&gt;$bt&lt;/span&gt;[&lt;span style="color:#00D"&gt;1&lt;/span&gt;][&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;args&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;][&lt;span style="color:#950"&gt;$i&lt;/span&gt;];
  &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#069"&gt;true&lt;/span&gt;;
}

&lt;span style="color:#369;font-weight:bold"&gt;set_error_handler&lt;/span&gt;(&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;changeArg&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span style="color:#C00;font-weight:bold"&gt;E_ALL&lt;/span&gt;);

f(&lt;span style="color:#00D"&gt;3&lt;/span&gt;, &lt;span style="color:#00D"&gt;2&lt;/span&gt;, &lt;span style="color:#00D"&gt;1&lt;/span&gt;);
&lt;/pre&gt;
&lt;p&gt;Outputs:&lt;/p&gt;
&lt;pre class="src" lang="text"&gt;
[4,2,1]
[4,3,2]
&lt;/pre&gt;
&lt;p&gt;Note that the required argument changes, but the optional argument does not.
  Also note that func_get_args() returns the altered argument, in disagreement
  with the actual argument.&lt;/p&gt;
&lt;p&gt;Where it gets interesting is if we alter that $i to be $i+1.  That is, if we
  change the argument &lt;i&gt;after&lt;/i&gt; the one we got an error about.&lt;/p&gt;
&lt;pre class="src" lang="php"&gt;
&lt;span style="font-weight:bold;color:#666"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;f&lt;/span&gt;(&lt;span style="color:#0a8;font-weight:bold"&gt;int&lt;/span&gt; &lt;span style="color:#950"&gt;$a&lt;/span&gt;, &lt;span style="color:#0a8;font-weight:bold"&gt;int&lt;/span&gt; &lt;span style="color:#950"&gt;$b&lt;/span&gt; = &lt;span style="color:#069"&gt;null&lt;/span&gt;, &lt;span style="color:#0a8;font-weight:bold"&gt;int&lt;/span&gt; &lt;span style="color:#950"&gt;$c&lt;/span&gt; = &lt;span style="color:#069"&gt;null&lt;/span&gt;) {
  &lt;span style="color:#369;font-weight:bold"&gt;error_log&lt;/span&gt;(json_encode([&lt;span style="color:#950"&gt;$a&lt;/span&gt;, &lt;span style="color:#950"&gt;$b&lt;/span&gt;, &lt;span style="color:#950"&gt;$c&lt;/span&gt;]));
  &lt;span style="color:#369;font-weight:bold"&gt;error_log&lt;/span&gt;(json_encode(&lt;span style="color:#369;font-weight:bold"&gt;func_get_args&lt;/span&gt;()));
}

&lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;changeArg&lt;/span&gt;(&lt;span style="color:#950"&gt;$code&lt;/span&gt;, &lt;span style="color:#950"&gt;$str&lt;/span&gt;) {
  &lt;span style="color:#369;font-weight:bold"&gt;preg_match&lt;/span&gt;(&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;/^Argument (&lt;/span&gt;&lt;span style="color:#b0b"&gt;\\&lt;/span&gt;&lt;span style="color:#D20"&gt;d+) /&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span style="color:#950"&gt;$str&lt;/span&gt;, &lt;span style="color:#950"&gt;$m&lt;/span&gt;);
  &lt;span style="color:#950"&gt;$i&lt;/span&gt; = &lt;span style="color:#950"&gt;$m&lt;/span&gt;[&lt;span style="color:#00D"&gt;1&lt;/span&gt;]-&lt;span style="color:#00D"&gt;1&lt;/span&gt;;
  &lt;span style="color:#950"&gt;$bt&lt;/span&gt; = &lt;span style="color:#369;font-weight:bold"&gt;debug_backtrace&lt;/span&gt;(&lt;span style="color:#069"&gt;null&lt;/span&gt;, &lt;span style="color:#00D"&gt;2&lt;/span&gt;);
  &lt;span style="color:#080;font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#369;font-weight:bold"&gt;count&lt;/span&gt;(&lt;span style="color:#950"&gt;$bt&lt;/span&gt;[&lt;span style="color:#00D"&gt;1&lt;/span&gt;][&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;args&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;]) &amp;gt; &lt;span style="color:#950"&gt;$i&lt;/span&gt;+&lt;span style="color:#00D"&gt;1&lt;/span&gt;) {
    ++&lt;span style="color:#950"&gt;$bt&lt;/span&gt;[&lt;span style="color:#00D"&gt;1&lt;/span&gt;][&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;args&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;][&lt;span style="color:#950"&gt;$i&lt;/span&gt;+&lt;span style="color:#00D"&gt;1&lt;/span&gt;];
  }
  &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#069"&gt;true&lt;/span&gt;;
}

&lt;span style="color:#369;font-weight:bold"&gt;set_error_handler&lt;/span&gt;(&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;changeArg&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span style="color:#C00;font-weight:bold"&gt;E_ALL&lt;/span&gt;);

f(&lt;span style="color:#00D"&gt;3&lt;/span&gt;, &lt;span style="color:#00D"&gt;2&lt;/span&gt;, &lt;span style="color:#00D"&gt;1&lt;/span&gt;);
&lt;/pre&gt;
&lt;p&gt;Outputs:&lt;/p&gt;
&lt;pre class="src" lang="text"&gt;
[3,3,2]
[3,3,2]
&lt;/pre&gt;
&lt;p&gt;Then the change sticks, and the optional argument is modified.&lt;/p&gt;
&lt;p&gt;By switching to $i-1, you&amp;#8217;ll notice that even the required argument fails to be
  modified.  Which means in the default case where one tries to modify the
  argument an error was triggered about, the argument&amp;#8217;s reference is being broken
  slightly earlier for optional arguments than for required arguments.  That, to
  me, strongly suggests a bug, because the behavior is inconsistent.&lt;/p&gt;
&lt;p&gt;But my use-case isn&amp;#8217;t supported, and presumably it doesn&amp;#8217;t affect any other
  use-cases, so I&amp;#8217;ll either have to live with it or write a code pre-processor
  which fixes up the issue.  Ah well.&lt;/p&gt;

&lt;p&gt;I'm left confused about why there exists such a thing as continuable errors if fixing the error and continuing on isn't supported, but ... PHP isn't exactly known for it's strong sense of feature coherency.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="https://www.dreamwidth.org/tools/commentcount?user=pinterface&amp;ditemid=2662" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/&gt; comments</content>
  </entry>
  <entry>
    <id>tag:dreamwidth.org,2012-03-19:1579314:2408</id>
    <link rel="alternate" type="text/html" href="https://pinterface.dreamwidth.org/2408.html"/>
    <link rel="self" type="text/xml" href="https://pinterface.dreamwidth.org/data/atom/?itemid=2408"/>
    <title>The Terrible, Horrible, No-Good, Very-Bad Things I Do To PHP (Or: Greenspunning in PHP)</title>
    <published>2014-08-26T01:22:39Z</published>
    <updated>2014-09-03T02:37:04Z</updated>
    <category term="lisp"/>
    <category term="php"/>
    <category term="programming"/>
    <category term="work"/>
    <dw:security>public</dw:security>
    <dw:reply-count>0</dw:reply-count>
    <content type="html">&lt;h3&gt;Controller Methods&lt;/h3&gt;
&lt;p&gt;What we call a &amp;#8220;controller method&amp;#8221;, at least, is simply a function which is
  called on the web side via AJAX and returns some value back (generally either
  JSON or a snippet of HTML).&lt;/p&gt;
&lt;p&gt;When I started at my current employer, controller methods looked like this:&lt;/p&gt;
&lt;pre class="src" lang="php"&gt;
&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;MyController&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;extends&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;Controller&lt;/span&gt; {
  &lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;f&lt;/span&gt;() {
    &lt;span style="color:#080;font-weight:bold"&gt;if&lt;/span&gt; (!&lt;span style="color:#950"&gt;$this&lt;/span&gt;-&amp;gt;required(&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;foo&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;bar&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;baz&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;)) &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt;;
    &lt;span style="color:#080;font-weight:bold"&gt;if&lt;/span&gt; (!&lt;span style="color:#369;font-weight:bold"&gt;is_numeric&lt;/span&gt;(&lt;span style="color:#950"&gt;$this&lt;/span&gt;-&amp;gt;requestvars[&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;foo&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;])) &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt;;
    &lt;span style="color:#777"&gt;// lots of code&lt;/span&gt;
    &lt;span style="color:#080;font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#369;font-weight:bold"&gt;isset&lt;/span&gt;(&lt;span style="color:#950"&gt;$this&lt;/span&gt;-&amp;gt;requestvars[&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;quux&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;])) {
      &lt;span style="color:#777"&gt;// do something with quux&lt;/span&gt;
    }
    &lt;span style="color:#777"&gt;// more code&lt;/span&gt;
    &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#950"&gt;$someString&lt;/span&gt;;
  }
}
&lt;/pre&gt;
&lt;p&gt;Aside from being hideous, this has a number of glaring problems:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The error handling is terrible.&lt;/li&gt;
  &lt;li&gt;Validation is hard, and thus incredibly easy to screw up or forget entirely.&lt;/li&gt;
  &lt;li&gt;Even something that should be easy―merely figuring out how to call the
    method―requires reading and understanding the entire method.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This just won&amp;#8217;t do.&lt;/p&gt;
&lt;p&gt;Surely it would be much nicer if controller methods could simply be defined like
  a regular function.  Fortunately, PHP offers some manner of reflexive
  capabilities, meaning we can ask it what arguments a function takes.  We can
  then match up GET/POST parameters by name, and send the function the proper
  arguments.&lt;/p&gt;
&lt;p&gt;In other words, we can define the function more like:&lt;/p&gt;
&lt;pre class="src" lang="php"&gt;
&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;MyController&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;extends&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;Controller&lt;/span&gt; {
  &lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;f&lt;/span&gt;(&lt;span style="color:#950"&gt;$foo&lt;/span&gt;, &lt;span style="color:#950"&gt;$bar&lt;/span&gt;, &lt;span style="color:#950"&gt;$baz&lt;/span&gt;, &lt;span style="color:#950"&gt;$quux&lt;/span&gt; = &lt;span style="color:#069"&gt;null&lt;/span&gt;) {
    &lt;span style="color:#777"&gt;// lots of code&lt;/span&gt;
    &lt;span style="color:#080;font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#369;font-weight:bold"&gt;isset&lt;/span&gt;(&lt;span style="color:#950"&gt;$quux&lt;/span&gt;)) {
      &lt;span style="color:#777"&gt;// do something with quux&lt;/span&gt;
    }
    &lt;span style="color:#777"&gt;// more code&lt;/span&gt;
    &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;Response&lt;/span&gt;\&lt;span style="color:#036;font-weight:bold"&gt;HTML&lt;/span&gt;(&lt;span style="color:#950"&gt;$someString&lt;/span&gt;);
  }
}
&lt;/pre&gt;
&lt;p&gt;And have it actually work.  That&amp;#8217;s much nicer.  Now, we can call the method from
  PHP as easily as we call it from JavaScript, and we don&amp;#8217;t have to read the
  entire function to figure out what arguments it takes.&lt;/p&gt;
&lt;p&gt;(The astute reader will also notice I&amp;#8217;ve moved to returning an object, so the
  response has a type.  This is super-handy, because now it&amp;#8217;s easy to ensure we
  send the apropriate content-type, enabling the JS side to do more intelligent
  things with it.)&lt;/p&gt;
&lt;p&gt;Of course, this only tells us which arguments it takes, and whether they&amp;#8217;re
  optional or required.  We still need easier data validation.  PHP provides type
  hints, but they only work for classes.  Or do they?&lt;/p&gt;
&lt;h3&gt;Type Hints&lt;/h3&gt;
&lt;p&gt;In a brazen display of potentially &lt;a href="http://php.net/manual/en/language.oop5.typehinting.php#83442"&gt;ill-advised hackery&lt;/a&gt; (our code is a little
  more involved, but that should give you the general idea), I added an error
  handler that enables us to define non-class types to validate things.&lt;/p&gt;
&lt;p&gt;So now we can do this:&lt;/p&gt;
&lt;pre class="src" lang="php"&gt;
&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;MyController&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;extends&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;Controller&lt;/span&gt; {
  &lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;f&lt;/span&gt;(
    &lt;span style="color:#0a8;font-weight:bold"&gt;int&lt;/span&gt; &lt;span style="color:#950"&gt;$foo&lt;/span&gt;,
    &lt;span style="color:#0a8;font-weight:bold"&gt;string&lt;/span&gt; &lt;span style="color:#950"&gt;$bar&lt;/span&gt;,
    &lt;span style="color:#0a8;font-weight:bold"&gt;string&lt;/span&gt; &lt;span style="color:#950"&gt;$baz&lt;/span&gt;,
    &lt;span style="color:#0a8;font-weight:bold"&gt;int&lt;/span&gt; &lt;span style="color:#950"&gt;$quux&lt;/span&gt; = &lt;span style="color:#069"&gt;null&lt;/span&gt;
  ) {
    &lt;span style="color:#777"&gt;// lots of code&lt;/span&gt;
    &lt;span style="color:#080;font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#369;font-weight:bold"&gt;isset&lt;/span&gt;(&lt;span style="color:#950"&gt;$quux&lt;/span&gt;)) {
      &lt;span style="color:#777"&gt;// do something with quux&lt;/span&gt;
    }
    &lt;span style="color:#777"&gt;// more code&lt;/span&gt;
    &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;Response&lt;/span&gt;\&lt;span style="color:#036;font-weight:bold"&gt;HTML&lt;/span&gt;(&lt;span style="color:#950"&gt;$someString&lt;/span&gt;);
  }
}
&lt;/pre&gt;
&lt;p&gt;And all the machinery ensures that by the time f() is executing, $foo looks like
  an integer, as does $quux if it was provided.&lt;/p&gt;
&lt;p&gt;Now the caller of the code can readily know what the value of the variables
  should look like, and the programmer of the function doesn&amp;#8217;t really have an
  excuse for not picking a type because it&amp;#8217;s so easy.&lt;/p&gt;
&lt;p&gt;Of course, this isn&amp;#8217;t sufficient yet either.  For instance, if I&amp;#8217;d like to be
  able to pass a date into the controller, it has to be a string.  Then the writer
  of the controller has to convert it to an appropriate class.  Surely it&amp;#8217;d be
  much nicer if the author of the controller method could say &amp;#8220;I want a DateTime
  object&amp;#8221;, which would be automagically converted from a specially-formatted
  string sent by the client.&lt;/p&gt;
&lt;h3&gt;Type Conversion via Typehints&lt;/h3&gt;
&lt;p&gt;Because PHP provides &lt;i&gt;references&lt;/i&gt; via the
  backtrace mechanism, we can modify the parameters a function was called with.&lt;/p&gt;
&lt;pre class="src" lang="php"&gt;
&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;MyController&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;extends&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;Controller&lt;/span&gt; {
  &lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;f&lt;/span&gt;(
    &lt;span style="color:#0a8;font-weight:bold"&gt;int&lt;/span&gt; &lt;span style="color:#950"&gt;$foo&lt;/span&gt;,
    &lt;span style="color:#0a8;font-weight:bold"&gt;string&lt;/span&gt; &lt;span style="color:#950"&gt;$bar&lt;/span&gt;,
    &lt;span style="color:#036;font-weight:bold"&gt;DateTime&lt;/span&gt; &lt;span style="color:#950"&gt;$baz&lt;/span&gt;,
    &lt;span style="color:#0a8;font-weight:bold"&gt;int&lt;/span&gt; &lt;span style="color:#950"&gt;$quux&lt;/span&gt; = &lt;span style="color:#069"&gt;null&lt;/span&gt;
  ) {
    &lt;span style="color:#777"&gt;// lots of code&lt;/span&gt;
    &lt;span style="color:#080;font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#369;font-weight:bold"&gt;isset&lt;/span&gt;(&lt;span style="color:#950"&gt;$quux&lt;/span&gt;)) {
      &lt;span style="color:#777"&gt;// do something with quux&lt;/span&gt;
    }
    &lt;span style="color:#777"&gt;// more code&lt;/span&gt;
    &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;Response&lt;/span&gt;\&lt;span style="color:#036;font-weight:bold"&gt;HTML&lt;/span&gt;(&lt;span style="color:#950"&gt;$someString&lt;/span&gt;);
  }
}
&lt;/pre&gt;
&lt;p&gt;So while $baz might be POSTed as baz=2014-08-16, what f() gets is a PHP DateTime
  object representing that date.  Due to the implementation mechanism, even
  something as simple as:&lt;/p&gt;
&lt;p&gt;$mycontroller-&amp;gt;f(1, &amp;#8220;bar&amp;#8221;, &amp;#8220;2014-08-16&amp;#8221;);&lt;/p&gt;
&lt;p&gt;Will result in $baz being a DateTime object inside f().&lt;/p&gt;
&lt;h4&gt;&lt;a name="typehint-arg-conversion-caveat"&gt;&lt;/a&gt;Caveat&lt;/h4&gt;
&lt;p&gt;There is an unfortunate caveat, and I have yet to figure out if it&amp;#8217;s a quirk of
  the way I implemented things, or a quirk in the way PHP is implemented, but
  optional arguments do not change.  That is,
  &lt;code&gt;SomeClass $var = null&lt;/code&gt;
  will result in $var still being a string.  func_get_args() will contain the
  altered value, however.&lt;/p&gt;
&lt;h3&gt;&lt;a name="multiple-inheritance-method-combos"&gt;&lt;/a&gt;Multiple Inheritance and Method Combinations&lt;/h3&gt;
&lt;p&gt;PHP is a single inheritance language.  Traits add some ability to build mixins,
  which is super-handy, but has some annoying restrictions.  Particularly around
  calling methods―in particular, you can&amp;#8217;t define a method in a trait, override it
  in a class which uses a trait, and then call the trait method from the class
  method.  At least, not easily and generally.&lt;/p&gt;
&lt;p&gt;Plus there&amp;#8217;s no concept of method combinations.  It&amp;#8217;d be really handy to be able
  to say &amp;#8220;hey, add this stuff to the return value&amp;#8221; (e.g., by appending to an
  array) and have it &lt;i&gt;just happen&lt;/i&gt;, rather than having to know how to combine your
  stuff with the parent method&amp;#8217;s stuff.&lt;/p&gt;
&lt;p&gt;While I&amp;#8217;m sad to say I don&amp;#8217;t have this working generally across any class, I
  have managed to get it working for a particular base class where it&amp;#8217;s most
  useful to our codebase.  Subclasses and traits can define certain methods, and
  when called, the class heirarchy will be automatically walked and the results of
  calling each method in the heirarchy will be combined.&lt;/p&gt;
&lt;pre class="src" lang="php"&gt;
trait &lt;span style="color:#036;font-weight:bold"&gt;BobsJams&lt;/span&gt; {
  &lt;span style="color:#080;font-weight:bold"&gt;static&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;BobsJams_getAdditionalJams&lt;/span&gt;() {
    &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; [ &lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;CranberryJam&lt;/span&gt;(), &lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;StrawberryJam&lt;/span&gt;() ];
  }
}

trait &lt;span style="color:#036;font-weight:bold"&gt;JimsJams&lt;/span&gt; {
  &lt;span style="color:#080;font-weight:bold"&gt;static&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;JimsJams_getAdditionalJams&lt;/span&gt;() {
    &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; [ &lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;BlackberryJam&lt;/span&gt;() ];
  }
}

&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;Jams&lt;/span&gt; {
  &lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;getJams&lt;/span&gt;() {
    &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; (&lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;MethodCombinator&lt;/span&gt;([], &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;span style="color:#D20"&gt;array_merge&lt;/span&gt;&lt;span style="color:#710"&gt;'&lt;/span&gt;&lt;/span&gt;))
      -&amp;gt;execute(&lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;ReflectionClass&lt;/span&gt;(&lt;span style="color:#369;font-weight:bold"&gt;get_called_class&lt;/span&gt;()), &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#D20"&gt;getAdditionalJams&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
  }
}

&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;FewJams&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;extends&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;Jams&lt;/span&gt; {
  &lt;span style="color:#080;font-weight:bold"&gt;static&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;getAdditionalJams&lt;/span&gt;() {
    &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; [ &lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;PineappleJam&lt;/span&gt;() ];
  }
}

&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;LotsOJams&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;extends&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;FewJams&lt;/span&gt; {
  &lt;span style="color:#080;font-weight:bold"&gt;use&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;BobsJams&lt;/span&gt;;
  &lt;span style="color:#080;font-weight:bold"&gt;use&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;JimsJams&lt;/span&gt;;

  &lt;span style="color:#080;font-weight:bold"&gt;static&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;getAdditionalJams&lt;/span&gt;() {
    &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; [ &lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;OrangeJam&lt;/span&gt;() ];
  }
}

(&lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;LotsOJams&lt;/span&gt;())-&amp;gt;getJams();
&lt;span style="color:#777"&gt;// =&amp;gt; [ OrangeJam, CranberryJam, StrawberryJam, BlackberryJam, PineappleJam ]&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;(The somewhat annoying prefix on the traits&amp;#8217; method names is to avoid forcing
  users of a trait to deal with name collisions.)&lt;/p&gt;
&lt;p&gt;Naturally, all the magic of the getJams() method is hidden away in the
  MethodCombinator class, but it just walks the class hierarchy―traits
  included―using the &lt;a href="https://en.wikipedia.org/wiki/C3_linearization"&gt;C3 Linearization&lt;/a&gt; algorithm, calls those methods, and then
  combines them all using the combinator function (in this case, array_merge).&lt;/p&gt;
&lt;p&gt;This, as you might imagine, greatly simplifies some code.&lt;/p&gt;
&lt;p&gt;Oh, but you&amp;#8217;re not impressed by shoehorning some level of multiple inheritance
  into a singly-inherited language?  Fine, how about&amp;#8230;&lt;/p&gt;
&lt;h3&gt;Context-Sensitive Object Behavior&lt;/h3&gt;
&lt;p&gt;Web code tends to be live, while mobile code
  is harshly asynchronous (as in: still needs to function when you have no signal,
  and then do something reasonable with data changes when you do have signal
  again), so what we care about changes between our Mobile API and our Web code,
  and yet we&amp;#8217;d still like to share the basic structure of any given piece of data
  so we don&amp;#8217;t have to write things twice or keep twice as much in our heads.&lt;/p&gt;
&lt;p&gt;Heavily inspired by Pascal Costanza&amp;#8217;s &lt;a href="http://common-lisp.net/project/closer/contextl.html"&gt;Context-Oriented-Programming&lt;/a&gt;, we define
  our data structures something like this:&lt;/p&gt;
&lt;pre class="src" lang="php"&gt;
&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;MyThing&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;extends&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;Struct&lt;/span&gt; {
  &lt;span style="color:#080;font-weight:bold"&gt;public&lt;/span&gt; &lt;span style="color:#950"&gt;$partA&lt;/span&gt;;
  &lt;span style="color:#080;font-weight:bold"&gt;public&lt;/span&gt; &lt;span style="color:#950"&gt;$userID&lt;/span&gt;;
  &lt;span style="color:#777"&gt;// ...&lt;/span&gt;
  &lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;getAdditionalDefaultContextualComponents&lt;/span&gt;() {
    &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; [ &lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;MyThingWebUI&lt;/span&gt;(), &lt;span style="color:#080;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;MyThingMobileAPI&lt;/span&gt;() ];
  }
}

&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;MyThingWebUI&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;extends&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;Contextual&lt;/span&gt; {
  &lt;span style="color:#080;font-weight:bold"&gt;public&lt;/span&gt; &lt;span style="color:#950"&gt;$isReadOnly&lt;/span&gt;;
  &lt;span style="color:#777"&gt;// ...&lt;/span&gt;
  &lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;getApplicableLayer&lt;/span&gt;() { &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#D20"&gt;WebUI&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;; }
}

&lt;span style="color:#080;font-weight:bold"&gt;class&lt;/span&gt; &lt;span style="color:#B06;font-weight:bold"&gt;MyThingMobileAPI&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;extends&lt;/span&gt; &lt;span style="color:#036;font-weight:bold"&gt;Contextual&lt;/span&gt; {
  &lt;span style="color:#080;font-weight:bold"&gt;public&lt;/span&gt; &lt;span style="color:#950"&gt;$partB&lt;/span&gt;;
  &lt;span style="color:#777"&gt;// ...&lt;/span&gt;
  &lt;span style="color:#080;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;getApplicableLayer&lt;/span&gt;() { &lt;span style="color:#080;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#D20"&gt;MobileAPI&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;; }
}
&lt;/pre&gt;
&lt;p&gt;The two Contextual subclasses define things that are only available within
  particular contexts (layers).  Thus, within the context of WebUI, MyThing
  appears from the outside to look like:&lt;/p&gt;
&lt;pre class="src" lang="json"&gt;
{
  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;partA&lt;/span&gt;&lt;span style="color:#404"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#D20"&gt;foo&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;userID&lt;/span&gt;&lt;span style="color:#404"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;: &lt;span style="color:#00D"&gt;12&lt;/span&gt;,
  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;isReadOnly&lt;/span&gt;&lt;span style="color:#404"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;: &lt;span style="color:#088"&gt;false&lt;/span&gt;,
}
&lt;/pre&gt;
&lt;p&gt;But within the Mobile API, that same $myThing object looks like:&lt;/p&gt;
&lt;pre class="src" lang="json"&gt;
{
  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;partA&lt;/span&gt;&lt;span style="color:#404"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#D20"&gt;foo&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;userID&lt;/span&gt;&lt;span style="color:#404"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;: &lt;span style="color:#00D"&gt;12&lt;/span&gt;,
  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;partB&lt;/span&gt;&lt;span style="color:#404"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color:#D20"&gt;bar&lt;/span&gt;&lt;span style="color:#710"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
}
&lt;/pre&gt;
&lt;p&gt;In addition to adding new properties, each layer can also exclude keys from JSON
  serialization, add aliases for keys (thus allowing mobiles to send/fetch data
  using old_key, when we rename something to new_key), and probably a few other
  things I&amp;#8217;m forgetting.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;PHP is remarkably malleable.  &lt;a href="http://php.net/manual/en/function.set-error-handler.php"&gt;error_handlers&lt;/a&gt; can be used as a poor-man&amp;#8217;s
  &lt;a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_handle.htm"&gt;handler-bind&lt;/a&gt; (unlike exceptions, they run before the stack is unwound, but
  you&amp;#8217;re stuck dispatching on regular expressions if you want more than one);
  scalar type hints can be provided as a library; and traits can be abused to
  provide a level of multiple inheritance well beyond what was intended.  While
  this malleability is certainly handy, I miss writing code in a language that
  doesn&amp;#8217;t require jumping through hoops to provide what feel like basic
  facilities.  But I&amp;#8217;m also incredibly glad I can draw from the well of ideas in
  Common Lisp and bring some of that into the lives of developers with less
  exposure to the fantastic facilities Lisp provides.&lt;/p&gt;
&lt;h3&gt;Bonus!&lt;/h3&gt;
&lt;p&gt;My employer is desperate for user feedback, and as such is offering a &lt;a href="https://www.bettrlife.com/free-trial-bettrlife/"&gt;free eight
  week trial&lt;/a&gt;.  So if you want to poke at stuff and mock me when things don&amp;#8217;t work
  very well (my core areas are nutritional analysis for recipes and food-related
  search results), that&amp;#8217;s a thing you can do.&lt;/p&gt;

&lt;p style="font-size: smaller;"&gt;If you're outside the US, I should warn you that we have a number of known bugs and shortcomings you're much more likely to hit (we use a US-based product database; searching for things outside ASCII doesn't work due to MySQL having columns marked as the wrong charset; and there's a lot of weirdness around time because most user times end up stored as unix timestamps).  The two bugs will be fixed eventually, but since they're complicated and as the US is our target market they're not exactly at the top of the list.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="https://www.dreamwidth.org/tools/commentcount?user=pinterface&amp;ditemid=2408" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/&gt; comments</content>
  </entry>
  <entry>
    <id>tag:dreamwidth.org,2012-03-19:1579314:2160</id>
    <link rel="alternate" type="text/html" href="https://pinterface.dreamwidth.org/2160.html"/>
    <link rel="self" type="text/xml" href="https://pinterface.dreamwidth.org/data/atom/?itemid=2160"/>
    <title>Things I've Learned from my Coworkers</title>
    <published>2014-08-21T04:00:10Z</published>
    <updated>2014-08-21T04:00:10Z</updated>
    <category term="work"/>
    <category term="programming"/>
    <dw:mood>reflective</dw:mood>
    <dw:security>public</dw:security>
    <dw:reply-count>1</dw:reply-count>
    <content type="html">&lt;aside style="font-style: italic;"&gt;&lt;p&gt;I started another post only to discover I had written this about a year ago and forgotten about it.  So while I spend some time polishing up my next post, enjoy my thoughts about my current job as I had them approximately a year in.&lt;/p&gt;&lt;/aside&gt;

&lt;p&gt;The job continues to go well.  Yay!  I've even managed to pick up a few lessons.&lt;/p&gt;

&lt;dl&gt;
&lt;dt title="Of course, sometimes it really is just bad code ;)"&gt;It's not bad code, it's just different&lt;/dt&gt;
&lt;dd&gt;As someone who has been historically quick to declare code crap, it's a refreshing change of pace to pick up on the attitude that maybe this code isn't crap, maybe I just don't understand it very well yet.  And I'm trying to take that to heart: there's plenty of things in the code base that aren't the way I'd have written them, but that doesn't make the way they're written any less valid.&lt;/dd&gt;
&lt;dt title="Turns out working for other people isn&amp;#39;t awful, so long as they&amp;#39;re good people"&gt;It's okay to go home&lt;/dt&gt;
&lt;dd&gt;In spite of working at a start-up, all of my coworkers are older than I am, and nearly all of them have families.  That means they go home; they understand being sick and prefer you stay home and not infect everyone else; they get that sometimes you need to call a plumber and will be working from home that day.&lt;/dd&gt;
&lt;dt&gt;Bring up issues, &lt;em&gt;then&lt;/em&gt; fix them&lt;/dt&gt;
&lt;dd&gt;Being a lone developer for so long, I got used to "see thing I think is problem, fix thing I think is problem".  I'm still trying to get the hang of when I can just do something and when I need to discuss it with other developers first.&lt;/dd&gt;
&lt;/dl&gt;

&lt;h3&gt;Of course, my coworkers aren't perfect&lt;/h3&gt;

&lt;dl&gt;
&lt;dt title="Really, what programmer doesn&amp;#39;t?"&gt;Sometimes they worry about micro-optimizations&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;Is it better to use autoloading, or explicit requires?  That leads to the question of which is faster, and regrettably, I fall into the same trap and respond in kind "Well, X is faster because of Y.".  The real answer, of course, is that it's a micro-optimization which is entirely irrelevant to the speed of our application&lt;sup&gt;&lt;a href="#fn-1-optimization"&gt;1&lt;/a&gt;&lt;/sup&gt; and we should be worried about which is easier to maintain.&lt;/p&gt;
&lt;p&gt;(NB: We've since switched entirely to autoloading for code maintenance reasons.)&lt;/p&gt;&lt;/dd&gt;
&lt;dt&gt;Limited experience "at scale"&lt;/dt&gt;
&lt;dd&gt;Nearly all the other developers worked together at a prior company building a C++ application where each installation served maybe a hundred users.  Naturally, this affects their perception of how to structure a program and what things are efficient, so there's a lot of functions which do things like return a resultset instead of a data structure to avoid the extra loop.  Much of the code passes around row ids, rather than objects or arrays of data, so the db gets queried for the same data a lot.  (We are making progress on this front, but new features tend to take priority.)&lt;/dd&gt;
&lt;dt&gt;Limited web experience&lt;/dt&gt;
&lt;dd&gt;I've found and either closed, or built tools which help avoid, numerous classes of web-specific security vulnerabilities.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;But overall, I have the distinct privilege of working with some intelligent people who are passionate about their work, have strong opinions formed through years of experience, who nonetheless still maintain an openness to trying new ways of doing things (and that's no platitude: I've spearheaded at least half a dozen major changes thus far&lt;sup&gt;&lt;a href="#fn-2-changes"&gt;2&lt;/a&gt;&lt;/sup&gt;).&lt;/p&gt;

&lt;aside style="font-style: italic;"&gt;&lt;p&gt;Me in the present here.  That openness―that lack of ego about one's own code―is something I continue to greatly appreciate, and strive to attain in myself.  I &lt;em style="font-style: normal;"&gt;do&lt;/em&gt; have an ego, and sometimes have to calm myself down when other people touch my code.  Fortunately, my coworkers provide great examples to follow in this regard.&lt;/p&gt;&lt;/aside&gt;

&lt;h3&gt;But then, neither am I&lt;/h3&gt;

&lt;dl&gt;
&lt;dt&gt;Have yet to fully assimilate the formatting convention&lt;/dt&gt;
&lt;dd&gt;The biggest annoyance to my coworkers is probably my habit of doing
&lt;code&gt;&lt;pre&gt;if (condition) do_thing();&lt;/pre&gt;&lt;/code&gt;
rather than
&lt;code&gt;&lt;pre&gt;if (condition) {
  do_thing();
}&lt;/pre&gt;&lt;/code&gt;
But it's certainly not the only formatting difference I haven't managed to avail myself of.  And even without formatting differences, I tend to write code in a very different style―if you ever come across the words 'curry' or 'filter' in our codebase, there are very good odds I touched that code.&lt;/dd&gt;
&lt;dt&gt;Or, really, any of the existing conventions&lt;/dt&gt;
&lt;dd&gt;Lowercase file names was the convention, until I introduced a lib/ directory and started filling it with mixed-case filenames which matched the case of the class names.&lt;/dd&gt;
&lt;dt&gt;Accidentally co-opted the name of the mobile API&lt;/dt&gt;
&lt;dd&gt;The API used by the phones was referred to as "the mobile API", or often just "API" for short.  I introduced a new convention for routing AJAX requests to controller methods, and called it the "web API", or usually just "API" for short.  This caused enough confusion that the mobile API was renamed "services".  I still wince a little whenever somebody says "services" because it's my fault.&lt;/dd&gt;
&lt;/dl&gt;

&lt;h3&gt;Of course, liking work does have some downsides&lt;/h3&gt;
&lt;p&gt;Because work is fulfilling, it's much easier to spend time working on work, rather than taking the hit of context-switching to a side-project on the weekends.  I sometimes manage to not work on weekends, but that's very different from working on something else.  So the various projects I maintain, or have plans to write, make no progress.&lt;/p&gt;

&lt;h3&gt;Footnotes&lt;/h3&gt;
&lt;ol style="margin-left: 0; padding-left: 0;"&gt;
&lt;li&gt;&lt;a name="fn-1-optimization"&gt;&lt;/a&gt;Our deployment servers have APC turned on and configured to not even bother checking the filesystem for updates.  That's a bigger performance win than any quibbling over autoloading-vs-require_once we could do, and even &lt;em&gt;that&lt;/em&gt; is a pretty negligible difference.&lt;/li&gt;
&lt;li&gt;&lt;a name="fn-2-changes"&gt;&lt;/a&gt;Our most recent hire has begun to spearheading a few, as well.  Hooray for new hires not yet being numb to the pain points!&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="https://www.dreamwidth.org/tools/commentcount?user=pinterface&amp;ditemid=2160" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/&gt; comments</content>
  </entry>
  <entry>
    <id>tag:dreamwidth.org,2012-03-19:1579314:1276</id>
    <link rel="alternate" type="text/html" href="https://pinterface.dreamwidth.org/1276.html"/>
    <link rel="self" type="text/xml" href="https://pinterface.dreamwidth.org/data/atom/?itemid=1276"/>
    <title>Upgrading the Linux Kernel when Using a USB Boot Disk</title>
    <published>2013-05-11T04:03:46Z</published>
    <updated>2013-05-11T04:03:46Z</updated>
    <category term="linux"/>
    <category term="infrequent processes"/>
    <category term="work"/>
    <dw:security>public</dw:security>
    <dw:reply-count>3</dw:reply-count>
    <content type="html">&lt;p&gt;In order to protect any sensitive data on my work laptop, I use full-drive encryption.  To aid recovery in case of theft, it normally boots to a honeypot installation of Windows with Prey installed.  To boot into Linux, one must use a USB boot disk&lt;a href="#kernel-update-usb-disk-footnote-1"&gt;[1]&lt;/a&gt; which contains the contents of /boot.&lt;/p&gt;

&lt;p&gt;Once in a while, it is necessary to upgrade the kernel.  I don't do this very often, so it seems like the sort of process which should be documented.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Mount /boot read-write: &lt;code&gt;`mount -o rw /boot`&lt;/code&gt;&lt;br&gt;
I generally have /boot mounted read-only, in part because I don't usually
have the thumbdrive plugged in (I don't boot very often, after all), and
to ensure I don't accidentally make changes to it without being prepared
to undergo this full process.&lt;/li&gt;
&lt;li&gt;Install kernel updates: &lt;code&gt;`sudo aptitude full-upgrade`&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Reboot.  Pray it works.&lt;/li&gt;
&lt;li&gt;Insert backup boot disk.  (Just in case the thumbdrive on my keychain goes bad or gets lost.)&lt;/li&gt;
&lt;li&gt;Copy files from the updated boot disk to the backup: &lt;code&gt;`rsync -av /boot /media/usb2`&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Remove the original boot disk.  This will help prevent unfortunate mistakes.&lt;/li&gt;
&lt;li&gt;Alter grub.cfg of backup boot disk to refer to the proper UUID:
&lt;code&gt;`sudo sed -i s/$UUID_OF_BOOT/$UUID_OF_BACKUP/ grub/grub.cfg`&lt;/code&gt;&lt;br&gt;
&lt;code&gt;`blkid`&lt;/code&gt; comes in handy for finding the relevant UUIDs.&lt;/li&gt;
&lt;li&gt;Reboot.  Pray it works.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Assuming both boot disks successfully boot the machine with the updated kernel, congratulations!  You have successfully updated the kernel!&lt;/p&gt;

&lt;p&gt;&lt;a name="kernel-update-usb-disk-footnote-1"&gt;&lt;/a&gt;[1] It's not quite as much a waste of a thumbdrive as you might think.  The sticks have a FAT partition of ~90% or so of the advertised capacity, so they can still be used to transfer files between computers or for data storage.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="https://www.dreamwidth.org/tools/commentcount?user=pinterface&amp;ditemid=1276" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/&gt; comments</content>
  </entry>
  <entry>
    <id>tag:dreamwidth.org,2012-03-19:1579314:338</id>
    <link rel="alternate" type="text/html" href="https://pinterface.dreamwidth.org/338.html"/>
    <link rel="self" type="text/xml" href="https://pinterface.dreamwidth.org/data/atom/?itemid=338"/>
    <title>Getting a Job, a Retrospective</title>
    <published>2013-01-13T01:31:03Z</published>
    <updated>2013-01-13T01:37:35Z</updated>
    <category term="work"/>
    <category term="lessons learned"/>
    <category term="personal growth"/>
    <dw:security>public</dw:security>
    <dw:reply-count>0</dw:reply-count>
    <content type="html">&lt;p&gt;Being largely burnt out on the &lt;a href="http://getbalm.com/"&gt;skin cream&lt;/a&gt; business, I decided near the beginning of 2012 it was time for a change of pace and decided to look for a job.&lt;/p&gt;

&lt;p&gt;Step one: &lt;a href="http://cialug.org/"&gt;Leave&lt;/a&gt; &lt;a href="http://pyowa.org/"&gt;the&lt;/a&gt; house.  Achievement unlocked!&lt;/p&gt;

&lt;p&gt;What I heard from people in these groups, and from various places on the Internet, is that now is a great time to be a programmer.  Jobs are easy to come by, they said!  The pay is great, they said!&lt;/p&gt;

&lt;p&gt;As it turns out, there are a few things that make it somewhat more difficult for me to find work: first, is location.  This town, which I would love to leave but not quite yet, thanks to the banks and insurance industry which dominate it, is a Java and .NET shop through and through, and they demand experience in those languages.  Also, banks and insurance companies are very stodgy, and maintain a considerably stricter dress code than I can bring myself to adhere to.  So for multiple reasons, this town's two primary industries are almost entirely out.&lt;/p&gt;

&lt;p&gt;So I'm largely left looking at the PHP, Python, and Ruby shops.  PHP because however much I hate it I have experience in it, and Python and Ruby because places which use Python and Ruby are enlightened enough to realize programmers can learn new programming languages.  That's a relatively small number of places in this town, but at least it's non-zero.&lt;/p&gt;

&lt;p&gt;Attending meetings is fun, but I'm terrible at networking (it requires talking, which I am wont not to do), so wasn't particularly helpful in terms of finding work.  But a &lt;a href="http://www.starmind.org/2012/04/07/so-you-want-a-new-job-adapted-from-a-presentation/"&gt;conveniently-time presentation&lt;/a&gt; by one of the CIALUG members on how he got a $20k/year pay hike with his new job proved interesting.&lt;/p&gt;

&lt;p&gt;First was the suggestion that, contrary to the opining on Hacker News, recruiters were not all terrible people, and a recommendation of the people at &lt;a href="http://www.thepalmergroup.com/"&gt;Palmer Group&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First sign of trouble: their baraccuda was doing deep packet inspection and rejected my e-mails on the basis of my home IP not being authorized to act as an SMTP server—that it isn't being used as one is, apparently, entirely irrelevant.  Had to craft a special rule to remove the Received: header before I could relay mail to them.  (Alas, postmaster@ was completely non-responsive.)&lt;/p&gt;

&lt;p&gt;Once I got through, my initial contact was quite nice.  Then I got handed off to a fellow who clearly was not interested.  But, with some encouraging from my initial contact, I met with him anyway and ... it did not go well.  We parted ways mutually unimpressed, and I never heard from him again.&lt;/p&gt;

&lt;p&gt;So much for &lt;em&gt;that&lt;/em&gt; recruiting agency.&lt;/p&gt;

&lt;p&gt;Second was the presenter's resume (alas, I cannot now find it), which I thought was pretty cool and modeled mine after.  Which is a pretty high compliment, actually: I've looked at resume examples online, and they all result in pretty much the same reaction: "This is boring and stupid.  Why the hell would anyone read this?".  That abundance of awful resumes kept me from even trying to make my own for years, and it wasn't until I saw the presenter's that I realized they could be not-terrible.&lt;/p&gt;

&lt;p&gt;Unfortunately for my desire for privacy, my resume isn't terribly impressive if restricted solely to work-related items, so I had to pad it out with a little open-source work.  I'd have vastly prefered to keep those lives separate (hence my not linking to a copy of my resume, though I've actually mentioned most of the things on it here), but felt I needed the extra oomph.  I expect my resume post-new-job to better hold up without violating that separation.&lt;/p&gt;

&lt;p&gt;There's not a lot of privacy in job hunting, actually.  People want things like your name.  Dice has the audacity to require a phone number and street address.  I'm an introverted nerd: I don't take phone calls, and I sure as heck don't want &lt;em&gt;mail&lt;/em&gt;.  Fortunately, "000-000-0000" and "Undisclosed" are considered valid inputs for those fields.&lt;/p&gt;

&lt;p&gt;Anyway, privacy issues aside, and resume in hand, I start looking for work.  So I peruse Craiglist want ads, and post my resume on Dice (the trick, of course, is that unlike &lt;a href="http://pinterface.livejournal.com/33362.html"&gt;last time&lt;/a&gt;, I'd actually like to end up with a job), and browse job sites.&lt;/p&gt;

&lt;p&gt;Great googly moogly are help wanted ads ever awful!  I can't even read more than a few a week, they're just so terrible.  They either leave me wondering what the job even &lt;em&gt;is&lt;/em&gt;, or they leave me wondering why anybody would &lt;em&gt;want&lt;/em&gt; it.&lt;/p&gt;

&lt;p&gt;So I send my resume out to a few places.  Nobody bites.  One actually sent me a "hey, thanks for sending us your resume", but otherwise I get nothing.  I periodically adjust my info on Dice, and see from my server logs that some of the links I helpfully included in my resume are being clicked on.&lt;/p&gt;

&lt;p&gt;Dice nets me two contacts at this point.  A recruiter and an interview request.&lt;/p&gt;

&lt;p&gt;The company requesting an interview doesn't sound all that interesting—just basic PHP site development—but I figure I could use the practice and go.  And I was right about needing the practice, because I bombed the interview, &lt;em&gt;hard&lt;/em&gt;.  Couldn't even accurately define &lt;a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller"&gt;MVC&lt;/a&gt;.  Naturally, they passed.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://executiveresources.com/"&gt;The recruiter&lt;/a&gt; was a nice lady who tried to get me a job at &lt;a href="https://www.webfilings.com/"&gt;WebFilings&lt;/a&gt; as a "Scrum Master", whatever that is.  I wasn't terribly thrilled about the commute, but she made a convincing argument for giving them a try.  But, after a month or two of her contacts being out of the office, they eventually decided to fill the position from within.&lt;/p&gt;

&lt;p&gt;Visibility supposedly helps, so I gave &lt;a href="http://pinterface.livejournal.com/tag/talks,python"&gt;my presentation at Pyowa&lt;/a&gt;.  That netted me an interview—and their head guy from Chicago seemed to really like me—, but for whatever reason, they too passed.  Which made me sad, because I really liked them and would likely have accepted in spite of the non-stellar pay because the work that was 3-6 months out (converting their monolithic architecture to a service-oriented one) sounded &lt;em&gt;really fun&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Somewhere in here I also send my resume to &lt;a href="http://www.businessolver.com/"&gt;a company&lt;/a&gt; which hosted one of the CIALUG meetings, because they said they were looking for people.  They do a little googling, end up on my father's personal site, and peruse the pictures page, presumably trying to remember who I was (I'm not recognizably there, but they don't know that) and possibly confusing me for my dad.  I get a polite brush-off.&lt;/p&gt;

&lt;p&gt;Monster is mentioned at a Pyowa meeting.  I sign up and post my resume.  And, while it results in vastly more contacts than Dice, they're all offers to become an insurance salesman.  And they have unsubscribe links, which should give you and idea of how personal they are.&lt;/p&gt;

&lt;p&gt;Suddenly, &lt;a href="http://www.aureusgroup.com/"&gt;another recruiter&lt;/a&gt; contacts me through Dice.  She loves my resume, then proceeds to ask questions which can largely be answered by reading it or my profile.  I grate my teeth and point this out as politely as I can while I answer the questions.  During the phone conversation, "How much do you make now?" "I'd rather not say." "Well I understand and maybe you'll tell me later."  (paraphrased), which I think is a little odd (the only thing about money anybody else has asked is how much I'd like to &lt;em&gt;get&lt;/em&gt;) but don't worry too much about.  I get asked to fill out a form online to get into their system.  This makes me not happy, but people aren't exactly beating down my door with money, so I go for it.  And their form wants information I can't give it (What month did I start?  It was &lt;em&gt;nine years ago&lt;/em&gt;, I don't remember!), and information I won't give it (current pay).  Not to be deterred, I e-mail the recruiter asking for suggestions.  That went something like this:&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;Recruiter&lt;/dt&gt;&lt;dd&gt;"You should tell us your current salary."&lt;/dd&gt;
&lt;dt&gt;Me&lt;/dt&gt;&lt;dd&gt;"No.  Stop asking or we're done here."&lt;/dd&gt;
&lt;dt&gt;Recruiter&lt;/dt&gt;&lt;dd&gt;"Okay, bye."&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;The exchange was somewhat more politely verbose, of course, but that's the gist of it.&lt;/p&gt;

&lt;p&gt;And then I pretty much forgot about Dice until I get an interview request from &lt;a href="https://www.bettrlife.com/"&gt;a little startup&lt;/a&gt; which enticed me with things like private offices and no dress code.  So I do the interview, and I have no idea how to judge it.  No real technical questions, no coding on the whiteboard, just "tell me about yourself / your work" (after a long pause: "I don't normally talk about myself, help me out here and ask me some questions!"), and chumming with a few of the existing coders (during which I did &lt;em&gt;very little&lt;/em&gt; of the talking).&lt;/p&gt;

&lt;p&gt;But they made an offer, and it wasn't half bad in terms of pay, either.  They agreed to a significant narrowing of the scope of their non-compete—which was originally so vague you could construe it as prohibiting clipping coupons from the newspaper—, and I accepted.&lt;/p&gt;

&lt;p&gt;So new job for Pixie!  It's not perfect—the commute is longer than I'd like (about 20 minutes), the company-provided computers are &lt;em&gt;laptops&lt;/em&gt; of all things, and they use Google apps&lt;sup&gt;&lt;a href="#google-apps-sucks"&gt;1&lt;/a&gt;&lt;/sup&gt;—, but it should be interesting.&lt;/p&gt;

&lt;p&gt;Here's some things I learned during this experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recruiters "love my resume", but if I could just go ahead and make it completely boring that'd be great.&lt;/li&gt;
&lt;li&gt;You can e-mail a link to your resume, using the e-mail as a cover letter.  Links to resumes do, in fact, get clicked.  (Handy for tracking!)&lt;/li&gt;
&lt;li&gt;Links &lt;em&gt;on resumes&lt;/em&gt; also get clicked.  (Also handy for tracking.)&lt;/li&gt;
&lt;li&gt;Visibility is a really good way to get companies interested.  It's also the most work.&lt;/li&gt;
&lt;li&gt;Monster may get you more contacts faster, but Dice will actually get &lt;em&gt;relevant&lt;/em&gt; contacts.&lt;/li&gt;
&lt;li&gt;Like dating, you get much better response rates from those who come to you.&lt;/li&gt;
&lt;li&gt;Wearing a t-shirt and shorts to an interview will not prevent you from getting an offer.&lt;/li&gt;
&lt;li&gt;Every interviewer will bring up Lisp if it's on the resume.&lt;/li&gt;
&lt;li&gt;Questions about the technology stack and development process tend to elicit a response of "that's a good question".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now, some months after accepting a position and working for a while (this entry was in my drafts folder so long I forgot about it!), I can add a few things to the list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Somewhere along the line, my Dice-tagged e-mail address was acquired by spammers.  Ugh.&lt;/li&gt;
&lt;li&gt;While I was worried having coworkers would reveal I'm a terrible programmer, turns out I'm not half-bad.  (Assuming my measurements are correct, my net effect on our primary repository has been roughly -74,000 lines, in spite of numerous feature expansions and improvements.)&lt;/li&gt;
&lt;li&gt;Working on a laptop isn't so bad.  My only real complaint is that they cheaped out on the screen, so it's got even fewer pixels than the already-pathetic "full HD" so many laptops tout as if it were decent.&lt;/li&gt;
&lt;li&gt;Working 40-50 hours a week kills nearly all of my free time.  I have yet to figure out how other people manage full-time work plus side projects.&lt;/li&gt;
&lt;li&gt;Having an office is awesome.  How anybody gets work done without the ability to make quiet something which was noisy is beyond me.&lt;/li&gt;
&lt;li&gt;I really like the new job.  Yay!&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;sup&gt;&lt;a name="google-apps-sucks"&gt;1&lt;/a&gt;&lt;/sup&gt; Good T.B.Lee, people use this crap?  None of it works with JavaScript disabled, and with JavaScript enabled it brings my computer to a griding halt.  And then some of it &lt;em&gt;still&lt;/em&gt; doesn't even work (go go Groups infinite redirect!).  Fortunately, I've managed to almost entirely avoid it.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="https://www.dreamwidth.org/tools/commentcount?user=pinterface&amp;ditemid=338" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/&gt; comments</content>
  </entry>
</feed>
