<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>npf.io</title><generator uri="https://gohugo.io">Hugo</generator><link>https://npf.io/</link><language>en-us</language><copyright>2026 Nate Finch</copyright><updated>Sun, 19 Apr 2026 12:01:33 -0400</updated><item><title>Optimizing for Reviewers: The Three Step AI Dev Loop</title><link>https://npf.io/2026/04/optimize-for-reviewers/</link><pubDate>Sun, 19 Apr 2026 12:01:33 -0400</pubDate><guid>https://npf.io/2026/04/optimize-for-reviewers/</guid><description>&lt;p&gt;With AI writing a lot more of our code, we need to optimize for the human code reviewer.&lt;/p&gt;
&lt;p&gt;The key to this is good test coverage and minimizing diffs in changes that humans need to verify.&lt;/p&gt;
&lt;p&gt;We should strive to separate refactors that don&amp;rsquo;t change logic from changes that intentionally change logic (bug fixes, feature additions, etc). Separating the two makes it significantly easier to understand if a change is correct. This was always best practice, but it was often burdensome because of the additional human effort required. Now with AI to do the boring parts, it&amp;rsquo;s more practical to put into practice.&lt;/p&gt;
&lt;p&gt;With broad enough test coverage, you can refactor the code all you want and still ensure it behaves the same. Then the code reviewer only needs to review the general shape of the new code to know if it&amp;rsquo;ll be fit for upcoming features.&lt;/p&gt;
&lt;p&gt;When adding new features, minimize the diff in production code as much as possible, even if the code is suboptimal. This makes it easier for reviewers to understand if the change is correct or not. You can always have the AI refactor it later.&lt;/p&gt;
&lt;p&gt;The best way to optimize for reviews is to break changes into three steps, each in separate PRs / commits that can be reviewed and verified independently.&lt;/p&gt;
&lt;h2 id="the-three-step-ai-dev-loop"&gt;The Three Step AI Dev Loop&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. Ensure tests validate current behavior, add tests if needed.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the production code as little as possible here.&lt;/li&gt;
&lt;li&gt;Write your tests to be as broad as possible, to make it easier to refactor later.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. Refactor the code so that it&amp;rsquo;s easier to add the feature you want.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Refactor as much as you can without changing tests at all, or with minimal, easy to verify changes.&lt;/li&gt;
&lt;li&gt;If you need to change the tests significantly to support a refactor, go back to #1.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. Write the feature/bugfix.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have the AI write the failing test for the new code first.&lt;/li&gt;
&lt;li&gt;Have the AI then implement the feature/bugfix in a way that minimizes the diff from the old production code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For #1, You want as close to zero production code changes as possible so that you know you&amp;rsquo;re just validating what already exists. You want tests that are likely to survive a refactor, or that will only need to change in minimal, easily verifiable ways.&lt;/p&gt;
&lt;p&gt;For #2 we refactor to make the code cleaner, more maintainable, more performant, and/or easier for the AI and humans to understand. It&amp;rsquo;s important not to change the tests so that humans know the behavior of the production code hasn&amp;rsquo;t changed. If small changes are needed and can be done in a way that is easy for AI and humans to verify, that&amp;rsquo;s fine (replacing a concrete type with an interface, for example).&lt;/p&gt;
&lt;p&gt;For #3, this is really just TDD, but the point is to minimize the diff, even to the point of suboptimal code with respect to performance, maintainability, etc. Since we&amp;rsquo;re changing behavior, we have to change both the tests and the production code at the same time, and this is where bugs can slip in. So we optimize for changes that are &lt;em&gt;most obviously correct&lt;/em&gt; beyond all other metrics. You want simple, straight-forward changes that are easy for a human to verify.&lt;/p&gt;
&lt;p&gt;After #3, you begin the loop again, and you can use #1 and #2 to refine the code from #3 to be actually-good code. Since the human knows the simple change in #3 was correct, and the tests fully constrain behavior, we can then refactor the obviously-correct code into more performant, more maintainable, more resilient code without worrying if the actual behavior is correct.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The key is,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you change prod, don&amp;rsquo;t change the tests.&lt;/li&gt;
&lt;li&gt;When you change the tests, don&amp;rsquo;t change prod.&lt;/li&gt;
&lt;li&gt;Any time you change both, minimize the diff and refactor later.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Why Infer Types?</title><link>https://npf.io/2022/05/why-infer/</link><pubDate>Thu, 26 May 2022 09:03:00 -0400</pubDate><guid>https://npf.io/2022/05/why-infer/</guid><description>&lt;p&gt;Someone at work asked the following question:&lt;/p&gt;
&lt;p&gt;Why write code like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;foo := getFoo()
bar, err := getBar()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;instead of this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;var foo Type = getFoo()
var err error
var bar Type
bar, err = getBar(foo)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Isn&amp;rsquo;t the latter more explicit? Isn&amp;rsquo;t explicit better? It&amp;rsquo;ll be easier to review
because you&amp;rsquo;ll be able to see all the types.&lt;/p&gt;
&lt;p&gt;Well, yes and no.&lt;/p&gt;
&lt;p&gt;For one thing, between the name of the function you’re calling and the name of
the variable you’re assigning to, the type is obvious most of the time, at
least at the high level.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;userID, err := store.FindUser(username)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Maybe you don’t know if userID is a UUID or some custom ID type, or even just a
numeric id&amp;hellip; but you know what it represents, and the compiler will ensure you
don’t send it to some function that doesn’t take that type or call a method on
it that doesn&amp;rsquo;t exist.&lt;/p&gt;
&lt;p&gt;In an editor, you’ll be able to hover over it to see the type. In a code
review, you may be able to as well, and even if not, you can rely on the
compiler to make sure it’s not being used inappropriately.&lt;/p&gt;
&lt;p&gt;One big reason to prefer the inferred type version is that it makes refactoring
a lot easier.&lt;/p&gt;
&lt;p&gt;If you write this code everywhere:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;var foo FooType = getFoo()
doSomethingWithFoo(foo)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now if you want to change getFoo to return a Foo2, and doSomethingWithFoo to
take a Foo2, you have to go change every place where these two functions are
called and update the explicitly declared variable type.&lt;/p&gt;
&lt;p&gt;But if you used inference:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;foo := getFoo()
doSomethingWithFoo(foo)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now when you change both functions to use Foo2, no other code has to change. And
because it’s statically typed, we can know this is safe, because the compiler
will make sure we can’t use Foo2 inappropriately.&lt;/p&gt;
&lt;p&gt;Does this code &lt;em&gt;really&lt;/em&gt; care what type getFoo returns, or what type
doSomethingWithFoo takes? No, it just wants to pipe the output of one into the
other. If this shouldn&amp;rsquo;t work, the type system will stop it.&lt;/p&gt;
&lt;p&gt;So, yes, please use the short variable declaration form. Heck, if you look at it
sideways, it even looks kinda like a gopher :=&lt;/p&gt;</description></item><item><title>Safer Enums</title><link>https://npf.io/2022/05/safer-enums/</link><pubDate>Fri, 13 May 2022 09:03:00 -0400</pubDate><guid>https://npf.io/2022/05/safer-enums/</guid><description>&lt;p&gt;How to &amp;ldquo;do&amp;rdquo; enums is a common problem in Go, given that it doesn&amp;rsquo;t have “real”
enums like other languages. There&amp;rsquo;s basically two common ways to do it, the
first is just typed strings:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;type FlagID string
const (
FooBar FlagID = “FooBar”
FizzBuzz FlagID = “FizzBuzz”
)
func IsEnabled(id FlagID) bool {
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The problem with this is that string literals (really, string constants) in Go
will get converted to the correct type, so you’d still be able to call
&lt;code&gt;IsEnabled(“foo-bar”)&lt;/code&gt; without the compiler complaining.&lt;/p&gt;
&lt;p&gt;A common replacement is to use numeric constants:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;type FlagID int
const (
FooBar FlagID = iota
FizzBuzz
)
func IsEnabled(id FlagID) bool {
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is nice, because it would be pretty odd to see code like &lt;code&gt;IsEnabled(4)&lt;/code&gt;.
But the problem then becomes that you can&amp;rsquo;t easily print out the name of the
enum in logs or errors.&lt;/p&gt;
&lt;p&gt;To fix this, someone (&lt;a href="https://go.dev/blog/generate"&gt;Rob Pike?&lt;/a&gt;) wrote
&lt;a href="https://pkg.go.dev/golang.org/x/tools/cmd/stringer"&gt;stringer&lt;/a&gt;, which generates
code to print out the name of the flags&amp;hellip; but then you have to remember to run
stringer, and it&amp;rsquo;s a bunch of (really) ugly code.&lt;/p&gt;
&lt;p&gt;The solution to this was something I first heard suggested by Dave Cheney
(because of course it was), and is so simple and effective that I can’t believe
I had never thought of it before. Make FlagName into a very simple struct:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;type FlagID struct {
name string
}
func (f FlagID) String() { return f.name }
var (
FooBar = FlagID{ “FooBar” }
FizzBuzz = FlagID{ “FizzBuzz” }
)
func IsEnabled(id FlagID) bool {
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, you can’t call &lt;code&gt;IsEnabled(“nope”)&lt;/code&gt;, because the constant string can’t be
converted into a struct, so the compiler would complain.&lt;/p&gt;
&lt;p&gt;There’s no size difference between a &lt;code&gt;string&lt;/code&gt; and a &lt;code&gt;struct{ string }&lt;/code&gt; and it&amp;rsquo;s
just as easy to read as a straight string. Because of the String() method, you
can pass these values to &lt;code&gt;%s&lt;/code&gt; etc in format strings and they&amp;rsquo;ll print out the
name with no extra code or work.&lt;/p&gt;
&lt;p&gt;The one tiny drawback is that the globals have to be variables instead of
constants, but that’s one of those problems that really only exists in the
theoretical realm. I’ve never seen a bug from someone overwriting a global
variable like this, that is intended to be immutable.&lt;/p&gt;
&lt;p&gt;I’ll definitely be using this pattern in my projects going forward. I hope this
helps some folks who are looking to avoid typos and accidental bugs from
stringly typed code in Go.&lt;/p&gt;</description></item><item><title>Error Flags</title><link>https://npf.io/2021/04/errorflags/</link><pubDate>Wed, 07 Apr 2021 23:03:00 -0400</pubDate><guid>https://npf.io/2021/04/errorflags/</guid><description>&lt;p&gt;Error wrapping in go 1.13 solved a major problem gophers have struggled with since v1: how to add context to errors without obscuring the original error, so that code above could programmatically inspect the original error. However, this did not – by itself – solve the other common problems with errors: implementation leakage and (more generally) error handling.&lt;/p&gt;
&lt;h2 id="fragile-error-handling"&gt;Fragile Error Handling&lt;/h2&gt;
&lt;p&gt;In 2016, Dave Cheney wrote &lt;a href="https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully"&gt;a blog post&lt;/a&gt; that includes a section titled “Assert errors for behaviour, not type”. The gist of the section is that you don’t want code to depend on implementation-specific error types that are returned from a package’s API, because then, if the implementation ever changes, the error handling code will break. Even four and a half years later, and with 1.13’s new wrapping, this can still happen very easily.&lt;/p&gt;
&lt;p&gt;For example, say you’re in an HTTP handler, far down the stack in your data layer. You’re trying to open a file and you get an os.ErrNotExist from os.Open. As of 1.13, you can add more context to that error without obscuring the fact that it’s an os.ErrNotExist. Cool, now the consumers of that code get a nicer error message, and if they want, they can check &lt;code&gt;os.IsNotExist(err)&lt;/code&gt; and maybe return a 404 to the caller.&lt;/p&gt;
&lt;p&gt;Right there, your web handler is now tied to the implementation details of how your backend, maybe 4 levels deep in the stack, stores data. If you decide to change your backend to store data in S3, and it starts returning &lt;code&gt;s3.ObjectNotFound&lt;/code&gt; errors, your web handler won’t recognize that error, and won’t know to return 404. This is barely better than matching on the error string.&lt;/p&gt;
&lt;h2 id="daves-solution---interfaces"&gt;Dave’s Solution - Interfaces&lt;/h2&gt;
&lt;p&gt;Dave proposes creating errors that fulfill interfaces the code can check for, like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;type notFound interface {
NotFound() bool
}
// IsNotFound returns true if err indicates the resource doesn’t exist.
func IsNotFound(err error) bool {
m, ok := err.(notFound)
return ok &amp;amp;&amp;amp; m.NotFound()
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Cool, so now you can ensure a consistent API without relying on the implementation-specific type of the error. Callers just need to check for IsNotFound, which could be fulfilled by any type. The problem is, it’s missing a piece. How do you take that os.NotExistErr and give it a IsNotFound() method? Well, it’s not super hard, but kind of annoying. You need to write this code:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// IsNotFound returns true if err indicates the resource doesn’t exist.
func IsNotFound(err error) bool {
n, ok := err.(notFound)
return ok &amp;amp;&amp;amp; n.NotFound()
}
// MakeNotFound wraps err in an error that reports true from IsNotFound.
func MakeNotFound(err error) error {
if err == nil {
return nil
}
return notFoundErr{error: err}
}
type notFound interface {
NotFound() bool
}
type notFoundErr struct {
error
}
func (notFoundErr) NotFound() bool {
return true
}
func (n notFoundErr) Unwrap() error {
return n.error
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So now we’re at 28 lines of code and two exported functions. Now what if you want the same for NotAuthorized or ? 28 more lines and two more exported functions. Each just to add one boolean of information onto an error. And that’s the thing… this is purely used for flow control - all it needs to be is booleans.&lt;/p&gt;
&lt;h2 id="a-better-way---flags"&gt;A Better Way - Flags&lt;/h2&gt;
&lt;p&gt;At Mattel, we had been following Dave’s method for quite some time, and our errors.go file was growing large and unwieldy. I wanted to make a generic version that didn’t require so much boilerplate, but was still strongly typed, to avoid typos and differences of convention.&lt;/p&gt;
&lt;p&gt;After thinking it over for a while, I realized it only took a slight modification of the above code to allow for the functions to take the flag they were looking for, instead of baking it into the name of the function and method. It’s of similar size and complexity to IsNotFound above, and can support expansion of the flags to check, with almost no additional work.
Here’s the code:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// ErrorFlag defines a list of flags you can set on errors.
type ErrorFlag int
const (
NotFound = iota + 1
NotAuthorized
// etc
)
// Flag wraps err with an error that will return true from HasFlag(err, flag).
func Flag(err error, flag ErrorFlag) error {
if err == nil {
return nil
}
return flagged{error: err, flag: flag}
}
// HasFlag reports if err has been flagged with the given flag.
func HasFlag(err error, flag ErrorFlag) bool {
for {
if f, ok := err.(flagged); ok &amp;amp;&amp;amp; f.flag == flag {
return true
}
if err = errors.Unwrap(err); err == nil {
return false
}
}
}
type flagged struct {
error
flag ErrorFlag
}
func (f flagged) Unwrap() error {
return f.error
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To add a new flag, you add a single line to the list of ErrorFlags and you move on. There’s only two exported functions, so the API surface is super easy to understand. It plays well with go 1.13 error wrapping, so you can still get at the underlying error if you really need to (but you probably won’t and shouldn’t!).&lt;/p&gt;
&lt;p&gt;Back to our example: the storage code can now keep its implementation private and flag errors from the backend with return errors.Flag(err, errors.NotFound). Calling code can check for that with this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;if errors.HasFlag(err, errors.NotFound) {
// handle not found
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the storage code changes what it’s doing and returns a different underlying error, it can still flag it with that with the NotFound flag, and the consuming code can go on its way without knowing or caring about the difference.&lt;/p&gt;
&lt;h2 id="supporting-errorsis-and-errorsas"&gt;Supporting Errors.Is and Errors.As&lt;/h2&gt;
&lt;p&gt;This is an update in 2022, and I realized that there&amp;rsquo;s an easier way to do this that properly supports errors.Is and errors.As. In go 1.20, there will be an &lt;a href="https://github.com/golang/go/issues/53435"&gt;errors.Join&lt;/a&gt; method that can let you combine two errors into one where either one will be found by &lt;code&gt;errors.Is&lt;/code&gt; and &lt;code&gt;errors.As&lt;/code&gt;. Until then, you can use &lt;a href="https://github.com/natefinch/wrap"&gt;github.com/natefinch/wrap&lt;/a&gt;. Then you can just define the flags as straight errors.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;package flags
var (
NotFound = errors.New(&amp;#34;not found&amp;#34;)
AlreadyExists = errors.New(&amp;#34;already exists&amp;#34;)
// etc
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, as long as the package wraps its errors with those flags (using a package like &lt;a href="https://github.com/natefinch/wrap"&gt;Wrap&lt;/a&gt; or the upcoming &lt;a href="https://github.com/golang/go/issues/53435"&gt;errors.Join&lt;/a&gt;), you can check for the flag with the normal functions:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; user, err := store.FindUser(id)
if errors.Is(err, flags.NotFound) {
// return 404
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The nice thing is that you can still get the behavior of the original error because this is non-destructive wrapping. So if you need some low level detail of the underlying error, you can get it.&lt;/p&gt;
&lt;h2 id="indirect-coupling"&gt;Indirect Coupling&lt;/h2&gt;
&lt;p&gt;Isn’t this just sentinel errors again? Well, yes, but that’s ok. In 2016, we didn’t have error wrapping, so anyone who wanted to add info to the error would obscure the original error, and then your check for err == os.ErrNotExist would fail. I believe that was the major impetus for Dave’s post. Error wrapping in Go 1.13 fixes that problem. The main problem left is tying error checks to a specific implementation, which this solves.&lt;/p&gt;
&lt;p&gt;This solution does require both the producer and the consumer of the error to import the error flags package and use these same flags, however in most projects this is probably more of a benefit than a problem. The edges of the application code can easily check for low level errors and flag them appropriately, and then the rest of the stack can just check for flags. Mattel does this when returning errors from calling the database, for example. Keeping the flags in one spot ensures the producers and consumers agree on what flag names exist.&lt;/p&gt;
&lt;p&gt;In theory, Dave’s proposal doesn’t require this coordination of importing the same package. However, in practice, you’d want to agree on the definition of IsNotFound, and the only way to do that with compile-time safety is to define it in a common package. This way you know no one’s going to go off and make their own IsMissing() interface that gets overlooked by your check for IsNotFound().&lt;/p&gt;
&lt;h2 id="choosing-flags"&gt;Choosing Flags&lt;/h2&gt;
&lt;p&gt;In my experience, there are a limited number of possible bits of data your code could care about coming back about an error. Remember, flags are only useful if you want to change the application’s behavior when you detect them. In practice, it’s not a big deal to just make a list of a handful of flags you need, and add more if you find something is missing. Chances are, you’ll think of more flags than you actually end up using in real code.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This solution has worked wonders for us, and really cleaned up our code of messy, leaky error handling code. Now our code that calls the database can parse those inscrutable postgres error codes right next to where they’re generated, flag the returned errors, and the http handlers way up the stack can happily just check for the NotFound flag, and return a 404 appropriately, without having to know anything about the database.&lt;/p&gt;
&lt;p&gt;Do you do something similar? Do you have a totally different solution? I’d love to hear about it in the comments.&lt;/p&gt;</description></item><item><title>How to Pay Remote Workers</title><link>https://npf.io/2019/07/how-to-pay-remote-workers/</link><pubDate>Fri, 26 Jul 2019 12:47:11 -0400</pubDate><guid>https://npf.io/2019/07/how-to-pay-remote-workers/</guid><description>&lt;p&gt;Pay remote workers the same as you&amp;rsquo;d pay local workers. Or vice versa if your
local workers are cheap.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it. That&amp;rsquo;s the blog post.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;It&amp;rsquo;s 2019, folks. Average home internet speeds are more than enough for video
conferencing and every single laptop has a built-in video camera. Conference
room video hardware has come way down in price and gone way up in quality.
Everyone collaborates via Slack and email and Jira and wikis and shared
documents in the cloud &lt;em&gt;anyway&lt;/em&gt;. Our code is hosted in the cloud, ci/cd in the
cloud, deployed to the cloud. Why on earth would it matter where your desk is?&lt;/p&gt;
&lt;p&gt;The truth is, it doesn&amp;rsquo;t matter. With extremely low effort, any company can hire
remote folks and have them be productive, collaborative members of a team. I
should know, I&amp;rsquo;ve done it for the last 8 years.&lt;/p&gt;
&lt;p&gt;One thing that always comes up with remote employees is &amp;ldquo;how much should I pay
them?&amp;rdquo; I&amp;rsquo;m not exactly sure why this is even a question&amp;hellip;. actually, yes, I am
sure. Because companies are cheap and want to pay employees as little as
possible. They are, after all, a business. So I guess the question is more
accurately asked &amp;ldquo;How can I justify paying my employees less while still getting
great talent?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The answer is always the same - cost of living adjustments. The theory is that
you pay everyone equitably, so they all sustain the same standard of living.
i.e. you pay the person in San Francisco enough for rent and food and spending
money for a new XBox every month. You do the same for the person in rural Ohio -
rent, food, Xbox every month.&lt;/p&gt;
&lt;p&gt;Just like a meritocracy, on its face, this sounds perfectly fair. But peek under
the surface, and it&amp;rsquo;s easily dismissed as false equivalence. &lt;em&gt;Why&lt;/em&gt; is a SF
apartment four times the cost of the same apartment in rural Ohio? Because of
supply and demand. Because people &lt;em&gt;believe&lt;/em&gt; the apartment in SF is worth more,
so they&amp;rsquo;re willing to pay more. Why do they believe that? Because the apartment
in SF is near awesome restaurants, easy public transportation, lots of great
similar-minded folks, etc. etc.&lt;/p&gt;
&lt;p&gt;These are attributes of the apartment that don&amp;rsquo;t fit on a spreadsheet of square
footage, number of bedrooms, and lot size&amp;hellip; but they have a huge effect on the
price of the home. Clearly, &lt;em&gt;that&lt;/em&gt; is what you&amp;rsquo;re paying for when you buy a
$500k studio in SF.&lt;/p&gt;
&lt;p&gt;So, if the house in SF is clearly more valuable than an equivalent-sized one in
rural Ohio&amp;hellip; why should the company subsidize paying for those invisible
benefits that come with a house in SF? Would you pay someone more who lived in a
bigger house in the same city? Why not? Why is it ok for companies to subsidize
the location-based value of a home, but not the value derived from
square-footage or lot size?&lt;/p&gt;
&lt;p&gt;To put a finer point on it&amp;hellip; would you pay someone less who lives on the wrong
side of the tracks in the same city? That&amp;rsquo;s still location-based, isn&amp;rsquo;t it?&lt;/p&gt;
&lt;p&gt;The thing is, the value of money isn&amp;rsquo;t actually different in SF and rural Ohio.
Buying an XBox from Amazon costs the same in both places. $3000 a month in rent
for a studio or $3000 a month in mortgage for a 4 bedroom house&amp;hellip;. still costs
you $3000. If you live in SF, you&amp;rsquo;re saying that studio&amp;rsquo;s location is worth
$3000 a month to you. If you live in rural Ohio, you&amp;rsquo;re saying the extra
bedrooms and big backyard are worth $3000 a month to you.&lt;/p&gt;
&lt;p&gt;&amp;hellip;so why would you pay the person in Ohio less?&lt;/p&gt;
&lt;p&gt;Someone on Twitter mentioned they understood paying people more who live in high
cost of living areas, but thought it would be weird to pay people less who live
in low cost of living areas&amp;hellip;. but it&amp;rsquo;s really the exact same thing. You pay
the person in SF more, and you&amp;rsquo;re just paying everyone else less. You can&amp;rsquo;t have
it one way and not the other.&lt;/p&gt;
&lt;p&gt;Does this mean you have to compete with Google&amp;rsquo;s salaries if your company is in
rural Ohio? Yes and no. It&amp;rsquo;s true that Google and the other big-five tech
companies pay people a lot more. But that&amp;rsquo;s true even in Silicon Valley. I&amp;rsquo;ve
interviewed at lots of SF companies that weren&amp;rsquo;t able to compete with those kind
of salaries either, but they still get to hire a lot of great talent. The big
five may have a lot of devs, but they can&amp;rsquo;t hire &lt;em&gt;all&lt;/em&gt; the devs. And since
hiring is really hard, they don&amp;rsquo;t even get all the best devs. The big five
mostly pay a lot of money to keep the other four from poaching&amp;hellip; i.e. they&amp;rsquo;re
really only competing with each other.&lt;/p&gt;
&lt;p&gt;So, you might not have to compete with the Googles of the world, but you
probably do have to compete with the Salesforces, Stripes, and (previous to
acquisition) Githubs. While those companies generally pay more than some random
tech company, it&amp;rsquo;s not double or triple. It&amp;rsquo;s like 30% more. And honestly,
developers are worth that much. Basically every company in existence needs
developers, or needs to pay a service vendor for specialized software.&lt;/p&gt;
&lt;p&gt;Hiring managers - the onus is on you to stop this predatory and unfair hiring
practice. Don&amp;rsquo;t accept it as &amp;ldquo;just the way things are&amp;rdquo;. Speak up against it.
Fight to get your remote developers the same salary and benefits your on-site
folks get. Their work is just as valuable to the company as the local folks,
paying them less is unfair, insulting, and wrong.&lt;/p&gt;</description></item><item><title>Do or Do Not</title><link>https://npf.io/2019/06/do-or-do-not/</link><pubDate>Fri, 07 Jun 2019 23:40:50 -0400</pubDate><guid>https://npf.io/2019/06/do-or-do-not/</guid><description>&lt;h2 id="the-proposal"&gt;The proposal&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a new Go proposal in town - &lt;a href="https://github.com/golang/go/issues/32437"&gt;try()&lt;/a&gt;. The gist is that it adds a builtin function &lt;code&gt;try()&lt;/code&gt; that can wrap a function that returns (a, b, c, &amp;hellip;, error), and if the error is non-nil, it will return from the enclosing function, and if the error is nil, it&amp;rsquo;ll return the rest of the return values.&lt;/p&gt;
&lt;p&gt;This is how it looks in code:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;func doIt() (string, int, error){
return &amp;#34;Daisy&amp;#34;, 45, io.EOF
}
func tryIt() error {
name, age := try(doIt())
// use name, age
return nil
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the above, if doIt returns a non-nil error, tryIt will exit at the point where try is called, and will return that error.&lt;/p&gt;
&lt;h2 id="complications"&gt;Complications&lt;/h2&gt;
&lt;p&gt;So here&amp;rsquo;s my problem with this&amp;hellip; it complicates the code. It adds points where your code can exit from inside the right hand side of a statement somewhere. It can make it very easy to miss the fact that there&amp;rsquo;s an early exit statement in the code.&lt;/p&gt;
&lt;p&gt;The above is simplistic, it could instead look like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;func tryIt() error {
fmt.Printf(&amp;#34;Hi %s, happy %vth birthday!\n&amp;#34;, try(doIt())
// do other stuff
return nil
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At first blush, it would be very easy to read that code and think this function
always returns nil, and that would be wrong and it could be catastrophically
wrong.&lt;/p&gt;
&lt;h2 id="the-old-way"&gt;The Old Way&lt;/h2&gt;
&lt;p&gt;In my opinion, the old way (below) of the original code is a lot more readable. The exit point is clearly called out by the return keyword as well as the indent. The intermediate variables make the print statement a lot more clear.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;func tryIt() error {
name, age, err := doIt()
if err != nil {
return err
}
fmt.Printf(&amp;#34;Hi %s, happy %vth birthday!\n&amp;#34;, name, age)
return nil
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Oh, and did you catch the mismatched parens on the Printf statement in the try() version of &lt;code&gt;tryIt()&lt;/code&gt; above? Me neither the first time.&lt;/p&gt;
&lt;h2 id="early-returns"&gt;Early Returns&lt;/h2&gt;
&lt;p&gt;Writing Go code involves a LOT of returning early, more than any other popular language except maybe C or Rust. That&amp;rsquo;s the real meat of all those &lt;code&gt;if err != nil&lt;/code&gt; statements&amp;hellip; it&amp;rsquo;s not the &lt;code&gt;if&lt;/code&gt;, it&amp;rsquo;s the &lt;strong&gt;return&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The reason early returns are so good is that once you pass that return block, you can ignore that case forever. The case where the file doesn&amp;rsquo;t exist? Past the line of os.Open&amp;rsquo;s error return, you can ignore it. It no longer exists as something you have to keep in your head.&lt;/p&gt;
&lt;p&gt;However, with try, you now have to worry about both cases in the same line and keep that in your head. Order of operations can come into play, how much work are you actually doing before this try may kick you out of the function?&lt;/p&gt;
&lt;h2 id="one-idea-per-line"&gt;One idea per line&lt;/h2&gt;
&lt;p&gt;One of the things I have learned as a go programmer is to eschew line density. I don&amp;rsquo;t want a whole ton of logic in one line of code. That makes it harder to understand and harder to debug. This is why I don&amp;rsquo;t care about missing ternary operator or map and filter generics. All those do is let you jam more logic into a single line, and I don&amp;rsquo;t want that. That makes code hard to understand, and easier to &lt;em&gt;misunderstand&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Try does exactly that, though. It encourages you to put a call getting data into a function that then uses that data. For simple cases, this is really nice, like field assignment:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;p := Person{
Name: try(getUserName()),
Age: try(getUserAge()),
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But note how even here, we&amp;rsquo;re trying to split up the code into multiple lines, one assignment per line.&lt;/p&gt;
&lt;p&gt;Would you ever write this code this way?&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;p := Person{Name: try(getUserName()), Age: try(getUserAge())}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You certainly can, but holy crap, that&amp;rsquo;s a dense line, and it takes me an order of magnitude longer to understand that line than it does the 4 lines above, even though they&amp;rsquo;re just differently formatted version of the exact same code. But this is exactly what will be written if try becomes part of the language. Maybe not struct initialization, but what about struct initialization functions?&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;p := NewPerson(try(getUserName()), try(getUserAge()))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nearly the same code. Still hard to read.&lt;/p&gt;
&lt;h2 id="nesting-functions"&gt;Nesting Functions&lt;/h2&gt;
&lt;p&gt;Nesting functions is bad for readability. I &lt;em&gt;very&lt;/em&gt; rarely nest functions in my go code, and looking at other people&amp;rsquo;s go code, most other people also avoid it. Not only does try() force you to nest functions as its basic use case, but it then encourages you to use that nested function nested in some other function. So we&amp;rsquo;re going from &lt;code&gt;NewPerson(name, age)&lt;/code&gt; to &lt;code&gt;NewPerson(try(getUserName()), try(getUserAge()))&lt;/code&gt;. And that&amp;rsquo;s a real tragedy of readability.&lt;/p&gt;</description></item><item><title>Hiring Remote</title><link>https://npf.io/2019/05/hiring-remote/</link><pubDate>Tue, 14 May 2019 23:05:55 -0400</pubDate><guid>https://npf.io/2019/05/hiring-remote/</guid><description>&lt;p&gt;I have been working remotely for about 8 years now. I&amp;rsquo;ve worked at companies
that did it poorly, and companies that did it well. Let me define remote for a
minute. I mean fully remote. Like, I can count on one hand the number of times per
year I see my coworkers in person and have fingers left over.&lt;/p&gt;
&lt;p&gt;I was the first remote employee in my division at Mattel, and I helped guide the
culture toward supporting remote employees. Mattel, for its part, has been very
supportive, and honestly did many things right without even really thinking about
them as supporting remote employees.&lt;/p&gt;
&lt;p&gt;I have a lot of thoughts about remote work, and I&amp;rsquo;ll probably turn this into a
series of posts on the subject. For now, I&amp;rsquo;m going to start at the beginning -
hiring.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve interviewed at over a dozen companies that support remote employees. How
the interviewing process goes tells me a lot about whether or not a company
&lt;em&gt;really&lt;/em&gt; supports remote employees.&lt;/p&gt;
&lt;p&gt;When I interviewed at Canonical, the whole interview was remote. I never saw
anyone in person until after I got my offer, and then it was just a run to the
nearest office to sign paperwork. I literally never met any of my coworkers in
person until our first offsite about three months in. And that&amp;rsquo;s totally ok.&lt;/p&gt;
&lt;p&gt;When I interviewed at Mattel it was much the same, except I was brought on as a
contractor first, which allowed me to prove myself, and then they were happy to
just continue letting me do my thing as a full time employee 3000 miles away
from the rest of the team. I pushed my boss to hire more remote devs, and we now
have a team that is almost fully remote.&lt;/p&gt;
&lt;p&gt;Many places I&amp;rsquo;ve interviewed want you to come onsite after some number of
interviews to &amp;ldquo;meet the team&amp;rdquo;. While this is ok, it tends to make me think those
places aren&amp;rsquo;t as fully bought into remote culture. There was no office to go
into at Canonical. Meeting the team was getting on a google hangout (and that&amp;rsquo;s
fine).&lt;/p&gt;
&lt;p&gt;If you buy into remote culture, meeting someone in a video chat should be good
enough. After all, that&amp;rsquo;s how you&amp;rsquo;re going to interact with them 99% of the
time. Just as the whiteboard is a relic of another time, I believe the onsite
interview is a relic if the job is remote. (If the job is not remote, then I
think it&amp;rsquo;s pretty important to get a handle on how the person interacts in
person with other people&amp;hellip; but that&amp;rsquo;s not what we&amp;rsquo;re talking about.)&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t code on a whiteboard at work, and I don&amp;rsquo;t code in a meeting room with
another developer at work either. And honestly, they almost never ask me to code
in that meeting room. It&amp;rsquo;s all talk and drawing architecture on a whiteboard.
Which, like, seriously, save yourself the plane ticket and hotel charge and just
let me do that over hangouts.&lt;/p&gt;
&lt;p&gt;The problem with having me come on site is that it&amp;rsquo;s a 2 day &lt;em&gt;thing&lt;/em&gt;. I have to
leave work early to catch a plane the night before, spend the night in a hotel,
get up mildly jetlagged, interview all day, then take a redeye home unless I
want to spend a second night in the hotel and get home at like 3 in the
afternoon the next day. If I did that for every job I interviewed for last time
I was looking, I would have had to take a full month off&amp;hellip; it&amp;rsquo;s just not
scalable&amp;hellip;. and it&amp;rsquo;s rough on my family.&lt;/p&gt;
&lt;p&gt;Speaking of family, let&amp;rsquo;s talk about onboarding. Some companies will onboard you
remotely. This is great. Paperwork can be tricky, but it&amp;rsquo;s doable with a notary public
(that&amp;rsquo;s what I did for Mattel). Otherwise, going onsite for onboarding is
fine&amp;hellip; you sign paperwork, get a company laptop, some swag, etc. All that
could be mailed out, but I get that paperwork can be tricky remote.&lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s like&amp;hellip; maybe 3 days if everything goes really slowly. Many places
want a week onsite for onboarding. Buh&amp;hellip;. to do what? If there are significant
things I can only do while onsite, we&amp;rsquo;re probably going to have problems when I
go back to work at my house for months at a time. Also, aren&amp;rsquo;t many of my
coworkers remote, so won&amp;rsquo;t most of them not even be there? One place even
mentioned onboarding was two weeks onsite.&lt;/p&gt;
&lt;p&gt;Two weeks is an eternity. I have young kids, and there is zero chance I&amp;rsquo;m going
anywhere onsite for two weeks. I work remote so I can be &lt;em&gt;with&lt;/em&gt; my kids. I&amp;rsquo;m
sure a lot of more senior devs out there are in the same position. Making your
onboarding process long makes you much less desirable to anyone with a family.
Don&amp;rsquo;t draw it out any longer than necessary. As a mediocre white man who is used
to getting his way, I might feel comfortable asking for a reduced onsite, but I
bet many other developers who are not so privileged might not.&lt;/p&gt;
&lt;p&gt;So, to sum up - if you really want to &lt;em&gt;show&lt;/em&gt; you support remote developers,
instead of just saying you do, start with the interview. Make as much of your
interview process remote as possible, and then make your onboarding as painless
as possible. It&amp;rsquo;ll save you time, it&amp;rsquo;ll save your candidates time, it&amp;rsquo;ll save
the company money, and it&amp;rsquo;ll make everyone happier.&lt;/p&gt;</description></item><item><title>Retooling Retool</title><link>https://npf.io/2019/05/retooling-retool/</link><pubDate>Sat, 11 May 2019 21:42:34 -0400</pubDate><guid>https://npf.io/2019/05/retooling-retool/</guid><description>&lt;p&gt;I was so happy when I discovered &lt;a href="https://github.com/twitchtv/retool"&gt;retool&lt;/a&gt;. It&amp;rsquo;s a go tool that builds and caches go binaries into a local directory so that your dev tools stay in sync across your team. It fixes all those problems where slight difference in binary versions produce different output and cause code churn. We use it at Mattel for our projects, because we tend to have a large number of external tools that we use for managing code generation, database migrations, release management, etc.&lt;/p&gt;
&lt;p&gt;However, retool &lt;a href="https://github.com/twitchtv/retool/issues/49#issuecomment-471622108"&gt;doesn&amp;rsquo;t work very well with modules&lt;/a&gt;, and trying to run it with modules turned off sometimes misbehaves, and some tools just fail to compile that way.&lt;/p&gt;
&lt;p&gt;So what to do? Well, it turns out that in the module world, retool can be replaced by a very small &lt;a href="https://github.com/magefile/mage"&gt;mage&lt;/a&gt; script:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;func Tools() error {
update, err := envBool(&amp;#34;UPDATE&amp;#34;)
if err != nil {
return err
}
if err := os.MkdirAll(&amp;#34;_tools&amp;#34;, 0700); err != nil {
return err
}
wd, err := os.Getwd()
if err != nil {
return err
}
env := map[string]string{&amp;#34;GOBIN&amp;#34;: filepath.Join(wd, &amp;#34;_tools&amp;#34;)}
args := []string{&amp;#34;get&amp;#34;}
if update {
args = []string{&amp;#34;get&amp;#34;, &amp;#34;-u&amp;#34;}
}
for _, t := range tools {
err := sh.RunWith(env, &amp;#34;go&amp;#34;, append(args, t)...)
if err != nil {
return err
}
}
return nil
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This code is pretty simple — it ensures the &lt;code&gt;_tools&lt;/code&gt; directory exists (which is where retool puts its binaries as well, so I just reused that spot since our .gitignore already ignored it). Then it sets &lt;code&gt;GOBIN&lt;/code&gt; to the _tools directory, so binaries built by the go tool will go there, and runs &lt;code&gt;go get importpath@&amp;lt;tag|hash&amp;gt;&lt;/code&gt;. That&amp;rsquo;s it. The first time, it&amp;rsquo;ll take a while to download all the libraries it needs to build the binaries into the modules cache, but after that it&amp;rsquo;ll figure out it doesn&amp;rsquo;t need to do anything pretty quick.&lt;/p&gt;
&lt;p&gt;Now just use the tool helper function below in your magefile to run the right versions of the binaries (and/or add _tools to your PATH if you use something like direnv).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// tool runs a command using a cached binary.
func tool(cmd string, args ...string) error {
return sh.Run(filepath.Join(&amp;#34;_tools&amp;#34;, cmd), args...)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now all the devs on your team will be using the same versions of their (go) dev tools, and you don&amp;rsquo;t even need a fancy third party tool to do it (aside from mage).
The list of tools then is just a simple slice of strings, thusly:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;var tools = []string{
&amp;#34;github.com/jteeuwen/go-bindata/go-bindata@6025e8de665b31fa74ab1a66f2cddd8c0abf887e&amp;#34;,
&amp;#34;github.com/golang/protobuf/protoc-gen-go@v1.3.1&amp;#34;,
&amp;#34;gnorm.org/gnorm@v1.0.0&amp;#34;,
&amp;#34;github.com/goreleaser/goreleaser@v0.106.0&amp;#34;,
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For most maintained libraries, you&amp;rsquo;ll get a nice semver release number in there, so it&amp;rsquo;s perfectly clear what you&amp;rsquo;re running (but for anything without tags, you can use a commit hash).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m really happy that this was as straightforward as I was hoping it would be, and it seems just as usable as retool for my use case.&lt;/p&gt;</description></item><item><title>Init Is Bad and You Should Feel Bad</title><link>https://npf.io/2019/04/init-is-bad-and-you-should-feel-bad/</link><pubDate>Fri, 12 Apr 2019 11:04:21 -0400</pubDate><guid>https://npf.io/2019/04/init-is-bad-and-you-should-feel-bad/</guid><description>&lt;p&gt;&lt;code&gt;func init()&lt;/code&gt; in Go is a weird beast. It&amp;rsquo;s the only function you can have
multiples of in the same package (yup, that&amp;rsquo;s right&amp;hellip; give it a try). It
gets run when the package is &lt;em&gt;imported&lt;/em&gt;. And you should never use it.&lt;/p&gt;
&lt;p&gt;Why not? Well, there&amp;rsquo;s a few reasons. The main one is that init is only useful
for setting global state. I think it&amp;rsquo;s pretty well accepted that global state is
bad (because it&amp;rsquo;s hard to test and it makes concurrency dangerous). So, by
association init is bad, because that&amp;rsquo;s all it can do.&lt;/p&gt;
&lt;p&gt;But wait, there&amp;rsquo;s more that makes it even worse. Init is run when a package is
imported, but when does a package get imported? If a imports b and b imports c
and b and c both have init functions, which one runs first? What if c has two
init functions in different files? You can find out, but it&amp;rsquo;s non-obvious and it
can change if you import code differently. Not knowing the order in which code
executes is bad. Normal go code executes top to bottom in a very clear and
obvious order. There&amp;rsquo;s good reason for that.&lt;/p&gt;
&lt;p&gt;How do you test init functions? Trick question, you can&amp;rsquo;t. It&amp;rsquo;s not possible to
test the state of a package before init and then make sure the state after init
is correct. As soon as your test code runs, it imports the package and runs init
right away. Ok, maybe that&amp;rsquo;s not 100% true, you can probably do some hackery in
init to check if you&amp;rsquo;re running under &lt;code&gt;go test&lt;/code&gt; and then not run the init logic&amp;hellip;
but then your package isn&amp;rsquo;t set up the way it expects, and you&amp;rsquo;d have to write a
test specifically named to run first, to test init&amp;hellip; and that&amp;rsquo;s just horrible
(and nobody does that, so it&amp;rsquo;s basically always untested code).&lt;/p&gt;
&lt;p&gt;Ok, so there&amp;rsquo;s the reasons not to use it&amp;hellip; now what do you do instead? If you
want state, use a struct. Instead of global variables on the package, use fields
on a struct. The package-level functions become methods, and the init function
becomes a constructor.&lt;/p&gt;
&lt;p&gt;This fixes all the aforementioned problems. You get rid of global variables, so
if you have two different parts of your code using the same package, they don&amp;rsquo;t
stomp on each other&amp;rsquo;s settings etc. You can run tests without worrying that a
previous test modifies global state for a later test. It&amp;rsquo;s clear and obvious how
to test before and after a constructor gets called. And finally, there&amp;rsquo;s a clear
and normal order to the initialization of things. You don&amp;rsquo;t have to wonder what
gets called when, because it&amp;rsquo;s just normal go functions.&lt;/p&gt;
&lt;p&gt;As a corollary&amp;hellip; this means you shouldn&amp;rsquo;t use underscore imports either (since
they&amp;rsquo;re generally only useful for triggering init functions). These imports
(&lt;code&gt;import _ &amp;quot;github.com/foo/db&amp;quot;&lt;/code&gt;) are used for their side effects, like
registering sql/db drivers. The problem is that these are, by definition,
setting global variables, and those are bad, as we&amp;rsquo;ve said. So don&amp;rsquo;t use those
either.&lt;/p&gt;
&lt;p&gt;Once you start writing code with structs instead of globals and init, you&amp;rsquo;ll
find your code is much easier to test, easier to use concurrently, and more
portable between applications. So, don&amp;rsquo;t use init.&lt;/p&gt;
&lt;p&gt;&amp;hellip;&lt;a href="https://twitter.com/TheMerovius"&gt;Axel Wagner&lt;/a&gt; mentioned on Twitter that this
looked too dogmatic, and he&amp;rsquo;s right. This is programming, there are infinite
possible programs, and thus there will always be exceptions to every rule. I
think it&amp;rsquo;s really rare that init is the right choice, and you should only come
to that decision after trying other options and ensuring you take into
consideration things like startup order, concurrent access, and testing.&lt;/p&gt;</description></item><item><title>Starlight</title><link>https://npf.io/2018/12/starlight/</link><pubDate>Fri, 07 Dec 2018 09:59:12 -0500</pubDate><guid>https://npf.io/2018/12/starlight/</guid><description>&lt;p&gt;I&amp;rsquo;d like to announce starlight - &lt;a href="https://github.com/starlight-go/starlight"&gt;https://github.com/starlight-go/starlight&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Starlight wraps google&amp;rsquo;s Go implementation of the &lt;a href="https://github.com/google/starlark-go"&gt;starlark python
dialect&lt;/a&gt; (most notably found in the Bazel build tool).
Starlight makes it super easy for users to extend your application by writing simple python-like
scripts that interact seamlessly with your current Go code&amp;hellip; with no boilerplate on your part.&lt;/p&gt;
&lt;h2 id="what-is-starlark"&gt;What is Starlark?&lt;/h2&gt;
&lt;p&gt;Starlark is a &lt;a href="https://github.com/google/starlark-go/blob/master/doc/spec.md"&gt;subset of python&lt;/a&gt; that removes some of the more advanced features, but keeps the easy to read-and-write feel. For the purposes of this article, to avoid confusion between starlight (my package) and starlark (the language), I&amp;rsquo;ll be referring to the code as python (since starlark code is a subset of python code), but there are some small differences (described in the previous link).&lt;/p&gt;
&lt;h2 id="parser-by-google"&gt;Parser by google&lt;/h2&gt;
&lt;p&gt;The parser and runner are maintained by google&amp;rsquo;s bazel team, which write starlark-go. Starlight is
a wrapper on top of that, which makes it so much easier to use starlark-go. The problem with the
starlark-go API is that it is more built to be a used as configuration, so it assumes you want to get
information out of starlark and into Go. It&amp;rsquo;s actually pretty difficult to get Go information into
a starlark script&amp;hellip;. unless you use starlight.&lt;/p&gt;
&lt;h2 id="easy-two-way-interaction"&gt;Easy two-way interaction&lt;/h2&gt;
&lt;p&gt;Starlight has adapters that use reflection to automatically make any Go value usable in a starlark
script. Passing an &lt;code&gt;*http.Request&lt;/code&gt; into a starlark script? Sure, you can do &lt;code&gt;name = r.URL.Query()[&amp;quot;name&amp;quot;][0]&lt;/code&gt; in the python without any work on your part.&lt;/p&gt;
&lt;p&gt;Starlight is built to &lt;em&gt;just work&lt;/em&gt; the way you hope it&amp;rsquo;ll work. You can access any Go methods or
fields, basic types get converted back and forth seamlessly&amp;hellip; and even though it uses reflection,
it&amp;rsquo;s not as slow as you&amp;rsquo;d think. A basic benchmark wrapping a couple values and running a starlark
script to work with them runs in a tiny fraction of a millisecond.&lt;/p&gt;
&lt;p&gt;The great thing is that the changes made by the python code are reflected in your go objects,
just as if it had been written in Go. So, set a field on a pointer to a struct? Your go code will
see the change, no additional work needed.&lt;/p&gt;
&lt;h2 id="100-safe"&gt;100% Safe&lt;/h2&gt;
&lt;p&gt;The great thing about starlark and starlight is that the scripts are 100% safe to run. By default
they have no access to other parts of your project or system - they can&amp;rsquo;t write to disk or connect
to the internet. The only access they have to the outside is what you give them. Because of this,
it&amp;rsquo;s safe to run untrusted scripts (as long as you&amp;rsquo;re not giving them dangerous functions to run,
like &lt;code&gt;os.RemoveAll&lt;/code&gt;). But at the same time, if you&amp;rsquo;re only running trusted scripts, you can give
them whatever you want (&lt;code&gt;http.Get&lt;/code&gt;? Sure, why not?)&lt;/p&gt;
&lt;h2 id="example"&gt;Example&lt;/h2&gt;
&lt;p&gt;Below is an example of a webserver that changes its output depending on the python script it runs. This is the full code, it&amp;rsquo;s not truncated for readability&amp;hellip; this is all it takes.&lt;/p&gt;
&lt;p&gt;First the go web server code. Super standard stuff, except a few lines to run starlight&amp;hellip;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;package main
import (
&amp;#34;fmt&amp;#34;
&amp;#34;log&amp;#34;
&amp;#34;net/http&amp;#34;
&amp;#34;github.com/starlight-go/starlight&amp;#34;
)
func main() {
http.HandleFunc(&amp;#34;/&amp;#34;, handle)
port := &amp;#34;:8080&amp;#34;
fmt.Printf(&amp;#34;running web server on http://localhost%v?name=starlight&amp;amp;repeat=3\n&amp;#34;, port)
if err := http.ListenAndServe(port, nil); err != nil {
log.Fatal(err)
}
}
func handle(w http.ResponseWriter, r *http.Request) {
fmt.Println(&amp;#34;handling request&amp;#34;, r.URL)
// here we define the global variables and functions we&amp;#39;re making available
// to the script. These will define how the script can interact with our Go
// code and the outside world.
globals := map[string]interface{}{
&amp;#34;r&amp;#34;: r,
&amp;#34;w&amp;#34;: w,
&amp;#34;Fprintf&amp;#34;: fmt.Fprintf,
}
_, err := starlight.Eval(&amp;#34;handle.star&amp;#34;, globals, nil)
if err != nil {
fmt.Println(err)
}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And the python handle.star:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Globals are:
# w: the http.ResponseWriter for the request
# r: the *http.Request
# Fprintf: fmt.Fprintf
# for loops and if statements need to be in functions in starlark
def main():
# Query returns a map[string][]string
# this gets a value from a map, with a default if it doesn&amp;#39;t exist
# and then takes the first value in the list.
repeat = r.URL.Query().get(&amp;#34;repeat&amp;#34;, [&amp;#34;1&amp;#34;])[0]
name = r.URL.Query().get(&amp;#34;name&amp;#34;, [&amp;#34;starlight&amp;#34;])[0]
for x in range(int(repeat)):
Fprintf(w, &amp;#34;hello %s\n&amp;#34;, name)
# we can use pythonic truthy statements on the slices returned from the map to
# check if they&amp;#39;re empty.
if not r.URL.Query().get(&amp;#34;repeat&amp;#34;) and not r.URL.Query().get(&amp;#34;repeat&amp;#34;):
w.Write(&amp;#34;\nadd ?repeat=&amp;lt;int&amp;gt;&amp;amp;name=&amp;lt;string&amp;gt; to the URL to customize this output\n&amp;#34;)
w.Write(&amp;#34;\ntry modifying the contents of output.star and see what happens.\n&amp;#34;)
main()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can run this example by running &lt;code&gt;go get github.com/starlight-go/starlight&lt;/code&gt; and using &lt;code&gt;go run main.go&lt;/code&gt; in the &lt;a href="https://github.com/starlight-go/starlight/tree/master/example"&gt;example folder&lt;/a&gt;.
You can then update the python and watch the changes the next time you hit the server. This just
uses &lt;code&gt;starlight.Eval&lt;/code&gt;, which rereads and reparses the script every time.&lt;/p&gt;
&lt;h2 id="caching"&gt;Caching&lt;/h2&gt;
&lt;p&gt;In a production environment, you probably want to only read a script once and parse it once. You
can do that with starlight&amp;rsquo;s &lt;code&gt;Cache&lt;/code&gt;. This cache takes a list of directories to look in for
scripts, which it will read and parse on-demand, and then store the parsed object in memory for
later use. It also uses a cache for any &lt;code&gt;load()&lt;/code&gt; calls the scripts use to load scripts they depend
on.&lt;/p&gt;
&lt;h2 id="work-ongoing"&gt;Work Ongoing&lt;/h2&gt;
&lt;p&gt;Starlight is still a work in progress, so don&amp;rsquo;t expect the API to be perfectly stable quite yet.
But it&amp;rsquo;s getting pretty close, and there shouldn&amp;rsquo;t be any earth shattering changes, but definitely
pin your imports. Right now it&amp;rsquo;s more about finding corner cases where the starlight wrappers don&amp;rsquo;t
work quite like you&amp;rsquo;d expect, and supporting the last few things that aren&amp;rsquo;t implemented yet (like
channels).&lt;/p&gt;</description></item><item><title>Mage - make/rake for Go</title><link>https://npf.io/2018/09/mage/</link><pubDate>Wed, 19 Sep 2018 22:50:51 -0400</pubDate><guid>https://npf.io/2018/09/mage/</guid><description>&lt;p align="center"&gt;&lt;img src="https://npf.io/gary.svg" width=50%/&gt;&lt;/p&gt;
&lt;h2 id="a-brief-history"&gt;A Brief History&lt;/h2&gt;
&lt;p&gt;A question came up at the Framingham Go meetup a while back about why something
like Gradle hasn&amp;rsquo;t taken hold in the Go community. I can&amp;rsquo;t say that I know for
sure what the answer is - I don&amp;rsquo;t speak for the community - but, I have some
guesses. I think part of it is that many projects don&amp;rsquo;t need a full-fledged
build tool - for your typical Go networked server or CLI tool, a single binary
built with &lt;code&gt;go build&lt;/code&gt; is probably fine.&lt;/p&gt;
&lt;p&gt;For more complex builds, which may require more steps than just compile and
link, like for bundling static assets in a web server or generating code from
protobufs, for example, many people in the Go community reach for Make.
Personally, I find that unfortunate. Makefiles are clearly pretty cool for a
number of reasons (built-in CLI, dependencies, file targets). However, Make is
not Windows friendly, and it has its own language and conventions that you need
to learn on top of the oddity that is Bash scripting. Finally, it doesn&amp;rsquo;t let
you leverage the Go community&amp;rsquo;s two greatest resources - go programmers and go
code.&lt;/p&gt;
&lt;h2 id="maybe-go-run--maybe-not"&gt;Maybe &lt;code&gt;go run&lt;/code&gt;? Maybe not&lt;/h2&gt;
&lt;p&gt;The above is the start of a blog post I&amp;rsquo;ve had half written for two years. I
started to go on to recommend using &lt;code&gt;go run make.go&lt;/code&gt; with a go file that does
the build for you. But in practice, this is problematic. If you want your
script to be useful for doing more than one thing, you need to implement a CLI
and subcommands. This ends up being a significant amount of work that then
obscures what the actual code is doing&amp;hellip; and no one wants to maintain yet
another CLI just for development tasks. In addition, there&amp;rsquo;s a lot of chaff you
have to handle, like printing out errors, setting up logging etc.&lt;/p&gt;
&lt;h2 id="the-last-straw"&gt;The Last Straw&lt;/h2&gt;
&lt;p&gt;Last summer there were a couple questions on
&lt;a href="https://reddit.com/r/golang"&gt;r/golang&lt;/a&gt; about best practices for using Makefiles
with Go&amp;hellip; and I finally decided I&amp;rsquo;d had enough.&lt;/p&gt;
&lt;p&gt;I looked around at what existed for alternatives -
&lt;a href="https://github.com/ruby/rake"&gt;rake&lt;/a&gt; was the obvious pattern to follow, being
very popular in the Ruby community. &lt;a href="http://www.pyinvoke.org/"&gt;pyinvoke&lt;/a&gt; was the
closest equivalent I saw in python. Was there something similar in Go? Well,
sort of, but not exactly. &lt;a href="https://github.com/go-task/task"&gt;go-task&lt;/a&gt; is
&lt;em&gt;written&lt;/em&gt; in Go, but tasks are actually defined in YAML. Not my
cup of tea. Mark Bates wrote &lt;a href="https://github.com/markbates/grift"&gt;grift&lt;/a&gt; which
has tasks written in Go, but I didn&amp;rsquo;t really like the ergonomics&amp;hellip; I wanted
just a little more magic.&lt;/p&gt;
&lt;p&gt;I decided that I could write a tool that behaved pretty similarly to Make, but
allowed you to write Go instead of Bash, and didn&amp;rsquo;t need any special syntax if
I did a little code parsing and generation on the fly. Thus, Mage was born.&lt;/p&gt;
&lt;h2 id="what-is-mage"&gt;What is Mage?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://magefile.org"&gt;magefile.org&lt;/a&gt;&lt;br/&gt;
&lt;strong&gt;Github:&lt;/strong&gt; &lt;a href="https://github.com/magefile/mage"&gt;github.com/magefile/mage&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mage is conceptually just like Make, except you write Go instead of Bash. Of
course, there&amp;rsquo;s a little more to it than that. In Mage, like in Make, you write
targets that can be accessed via a simple CLI. In Mage, exported functions
become targets. Any of these exported functions are then runnable by running
&lt;code&gt;mage &amp;lt;func_name&amp;gt;&lt;/code&gt; in the directory where the magefile lives, just like you&amp;rsquo;d run
&lt;code&gt;make &amp;lt;target_name&amp;gt;&lt;/code&gt; for a make target.&lt;/p&gt;
&lt;h2 id="what-is-a-magefile"&gt;What is a Magefile?&lt;/h2&gt;
&lt;p&gt;A magefile is simply a .go file with the mage build tag in it. All you need for
a magefile is this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//+build mage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mage looks for all go files in the current directory with the &lt;code&gt;mage&lt;/code&gt; build tag,
and compiles them all together with a generated CLI.&lt;/p&gt;
&lt;p&gt;There are a few nice properties that result from using a build tag to mark
magefiles - one is that you can use as many files as you like named whatever you
like. Just like in normal go code, the files all work together to create a
package.&lt;/p&gt;
&lt;p&gt;Another really nice feature is that your magefiles can live side by side with
your regular go code. Mage only builds the files with the mage tag, and your
normal go build only builds the files &lt;em&gt;without&lt;/em&gt; the mage tag.&lt;/p&gt;
&lt;h2 id="targets"&gt;Targets&lt;/h2&gt;
&lt;p&gt;A function in a magefile is a target if it is exported and has a signature of
&lt;code&gt;func()&lt;/code&gt;, &lt;code&gt;func()error&lt;/code&gt;, &lt;code&gt;func(context.Context)&lt;/code&gt;, or
&lt;code&gt;func(context.Context)error&lt;/code&gt;. If the target has an error return and you return
an error, Mage will automatically print out the error to its own stderr, and
exit with a non-zero error code.&lt;/p&gt;
&lt;p&gt;Doc comments on each target become CLI docs for the magefile, doc comments on
the package become top-level help docs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//+build mage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Mostly this is used for building the website and some dev tasks.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Builds the website. If needed, it will compact the js as well.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Build&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// do your stuff here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running mage with no arguments (or &lt;code&gt;mage -l&lt;/code&gt; if you have a default target
declared) will print out help text for the magefiles in the current directory.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ mage
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Mostly this is used for building the website and some dev tasks.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Targets:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; build Builds the website.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first sentence is used as short help text, the rest is available via &lt;code&gt;mage -h &amp;lt;target&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ mage -h build
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mage build:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Builds the website. If needed, it will compact the js as well.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This makes it very easy to add a new target to your magefile with proper
documentation so others know what it&amp;rsquo;s supposed to do.&lt;/p&gt;
&lt;p&gt;You can declare a default target to run when you run mage without a target very
easily:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Default&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;Build&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And just like Make, you can run multiple targets from a single command&amp;hellip; &lt;code&gt;mage build deploy clean&lt;/code&gt; will do the right thing.&lt;/p&gt;
&lt;h2 id="dependencies"&gt;Dependencies&lt;/h2&gt;
&lt;p&gt;One of the great things about Make is that it lets you set up a tree of
dependencies/prerequisites that must execute and succeed before the current
target runs. This is easily done in Mage as well. The
&lt;code&gt;github.com/magefile/mage/mg&lt;/code&gt; library has a &lt;code&gt;Deps&lt;/code&gt; function that takes a list of
dependencies, and runs them in parallel (and any dependencies they have), and
ensures that each dependency is run exactly once and succeeds before continuing.&lt;/p&gt;
&lt;p&gt;In practice, it looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Build&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;mg&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Deps&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;Generate&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Protos&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// do build stuff&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Generate&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;mg&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Deps&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;Protos&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// generate stuff&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Protos&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// build protos&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this example, build depends on generate and protos, and generate depends on
protos as well. Running build will ensure that protos runs exactly once, before
generate, and generate will run before build continues. The functions sent to
Deps don&amp;rsquo;t have to be exported targets, but do have to match the same signature
as targets have (i.e. optional context arg, and optional error return).&lt;/p&gt;
&lt;h2 id="shell-helpers"&gt;Shell Helpers&lt;/h2&gt;
&lt;p&gt;Running commands via os/exec.Command is cumbersome if you want to capture
outputs and return nice errors. &lt;code&gt;github.com/magefile/mage/sh&lt;/code&gt; has helper
methods that do all that for you. Instead of errors you get from exec.Command
(e.g. &amp;ldquo;command exited with code 1&amp;rdquo;), &lt;code&gt;sh&lt;/code&gt; uses the stderr from the command as
the error text.&lt;/p&gt;
&lt;p&gt;Combine this with the automatic error reporting of targets, and you easily get
helpful error messages from your CLI with minimal work:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Build&lt;/span&gt;() &lt;span style="color:#66d9ef"&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sh&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Run&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;go&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;build&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;-o&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;foo.out&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="verbose-mode"&gt;Verbose Mode&lt;/h2&gt;
&lt;p&gt;Another nice thing about the &lt;code&gt;sh&lt;/code&gt; package is that if you run mage with &lt;code&gt;-v&lt;/code&gt; to
turn on verbose mode, the &lt;code&gt;sh&lt;/code&gt; package will print out the args of what commands
it runs. In addition, mage sets up the stdlib &lt;code&gt;log&lt;/code&gt; package to default to
discard log messages, but if you run mage with -v, the default logger will
output to stderr. This makes it trivial to turn on and off verbose logging in
your magefiles.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How it Works&lt;/h2&gt;
&lt;p&gt;Mage parses your magefiles, generates a main function in a new file (which
contains code for a generated CLI), and then shoves a compiled binary off in a
corner of your hard drive. The first time it does this for a set of magefiles,
it takes about 600ms. Using the go tool&amp;rsquo;s ability to check if a binary needs to
be rebuilt or not, further runs of the magefile avoid the compilation overhead
and only take about 300ms to execute. Any changes to the magefiles or their
dependencies cause the cached binary to be rebuilt automatically, so you&amp;rsquo;re
always running the newest correct code.&lt;/p&gt;
&lt;p&gt;Mage is built 100% with the standard library, so you don&amp;rsquo;t need to install a
package manager or anything other than go to build it (and there are binary
releases if you just want to curl it into CI).&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve been using Mage for all my personal projects for almost a year and for
several projects at Mattel for 6 months, and I&amp;rsquo;ve been extremely happy with it.
It&amp;rsquo;s easy to understand, the code is plain old Go code, and it has just enough
helpers for the kinds of things I generally need to get done, taking all the
peripheral annoyances out of my way and letting me focus on the logic that needs
to be right.&lt;/p&gt;
&lt;p&gt;Give it a try, file some issues if you run into anything. Pull requests more
than welcome.&lt;/p&gt;</description></item><item><title>Handle and Check - Let's Not</title><link>https://npf.io/2018/09/check-and-handle/</link><pubDate>Thu, 06 Sep 2018 13:49:33 -0400</pubDate><guid>https://npf.io/2018/09/check-and-handle/</guid><description>&lt;p&gt;There&amp;rsquo;s a new error handling design &lt;a href="https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling.md"&gt;proposed here&lt;/a&gt;. It&amp;rsquo;s&amp;hellip;. not great.&lt;/p&gt;
&lt;p&gt;Handle is a new keyword that basically defines a translation that can be applied
to errors returned from the current function:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;func printSum(a, b string) error {
handle err { return fmt.Errorf(&amp;#34;error summing %v and %v: %v&amp;#34;, a, b, err ) }
x := check strconv.Atoi(a)
y := check strconv.Atoi(b)
fmt.Println(&amp;#34;result:&amp;#34;, x + y)
return nil
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Check applies the handler and returns if the error passed into it is not nil,
otherwise it returns the non-error value.&lt;/p&gt;
&lt;p&gt;Handle, in my opinion is kind of useless. We can already do this today with functions thusly:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;func printSum(a, b string) (err error) {
check := func(err error) error {
return fmt.Errorf(&amp;#34;error summing %v and %v: %v&amp;#34;, a, b, err )
}
x, err := strconv.Atoi(a)
if err != nil {
return check(err)
}
y, err := strconv.Atoi(b)
if err != nil {
return check(err)
}
fmt.Println(&amp;#34;result:&amp;#34;, x + y)
return nil
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That does literally the same thing as check and handle above.&lt;/p&gt;
&lt;p&gt;The stated reason for adding check and handle is that too many people just write
&amp;ldquo;return err&amp;rdquo; and don&amp;rsquo;t customize the error at all, which means somewhere at the
top of your program, you get this inscrutable error from deep in the bowels of
your code, and you have no idea what it actually means.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s trivial to write code that does most of what check and handle do&amp;hellip; and no
one&amp;rsquo;s doing it today (or at least, not often). So why add this complexity?&lt;/p&gt;
&lt;p&gt;Check and handle actually make error handling worse. With the check and handle
code, there&amp;rsquo;s no required &amp;ldquo;error handling scope&amp;rdquo; after the calls to add context
to the error, log it, clean up, etc. With the current code, I &lt;em&gt;always&lt;/em&gt; have an
if statement that I can easily slot more lines into, in order to make the error
more useful and do other things on the error path. With &lt;code&gt;check&lt;/code&gt;, that space in
the code doesn&amp;rsquo;t exist. There&amp;rsquo;s a barrier to making that code handle errors
better - now you have to remove &lt;code&gt;check&lt;/code&gt; and swap in an if statement. Yes,
you can add a new &lt;code&gt;handle&lt;/code&gt; section, but that applies globally to any further
errors returns in the function, not just for this one specific error. Most of
the time I want to add information about one specific error case.&lt;/p&gt;
&lt;p&gt;So, for example, in the code above, I would want a different error message for A
failing Atoi vs. B failing Atoi&amp;hellip;. because in real code, which one is the
problem may not be obvious if the error message just says &amp;ldquo;either A or B is a
problem&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Yes, &lt;code&gt;if err != nil {&lt;/code&gt; constitutes a lot of Go code. That&amp;rsquo;s ok. That&amp;rsquo;s actually
good. Error handling is extremely important. Check and handle don&amp;rsquo;t make error
handling better. I suspect they&amp;rsquo;ll actually make it worse.&lt;/p&gt;
&lt;p&gt;A refrain I often state about changes requested for Go is that most of them
just involve avoiding an if statement or a loop. This is one of them. That&amp;rsquo;s
not a good enough reason to change the language, in my opinion.&lt;/p&gt;</description></item><item><title>Go2 Contracts Go Too Far</title><link>https://npf.io/2018/09/go2-contracts-go-too-far/</link><pubDate>Wed, 05 Sep 2018 14:00:18 -0400</pubDate><guid>https://npf.io/2018/09/go2-contracts-go-too-far/</guid><description>&lt;p&gt;So, I don&amp;rsquo;t really like the contracts defined
&lt;a href="https://go.googlesource.com/proposal/+/master/design/go2draft-contracts.md"&gt;here&lt;/a&gt;.
They seem complicated to understand, and duplicate a lot of what interfaces
already do, but in a much clunkier fashion.&lt;/p&gt;
&lt;p&gt;I think we can do 90% of what the design given can do, with 20% of the added
complexity.&lt;/p&gt;
&lt;p&gt;Most of my objection comes from two things:&lt;/p&gt;
&lt;p&gt;First the syntax, which adds &amp;ldquo;type parameters&amp;rdquo; as yet another overloaded meaning
for stuff in parentheses (we already have: argument lists, return values,
function calls, type conversion, type assertion, and grouping for order of
operations).&lt;/p&gt;
&lt;p&gt;Second, the implicit nature of how contracts are defined by a random block of
code that is sorta like go code, but not actually go code.&lt;/p&gt;
&lt;h2 id="syntax"&gt;Syntax&lt;/h2&gt;
&lt;p&gt;This is a generic function as declared in the contracts code:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;func Print(type T)(s []T) {
for _, v := range s {
fmt.Println(v)
}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The (type T) here defines a type parameter. In this case it doesn&amp;rsquo;t tell us
anything about the type, so it&amp;rsquo;s effectively like interface{}, except that it
magically works with slices the way we all thought interfaces should work with
slices back in the day – i.e. you can pass any slice into this, not just
[]interface{}.&lt;/p&gt;
&lt;p&gt;Are we now going to have &lt;code&gt;func(type T)(input T)(output T){}&lt;/code&gt;? That&amp;rsquo;s crazy.&lt;/p&gt;
&lt;p&gt;Also, I don&amp;rsquo;t like that the type parameters precede the arguments&amp;hellip; isn&amp;rsquo;t the
whole reason that we have Go&amp;rsquo;s unusual &lt;name type&gt; ordering that we acknowledge
that the name is more important than the type?&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s my fix&amp;hellip; since contracts are basically like interfaces, let&amp;rsquo;s actually
use interfaces. And let&amp;rsquo;s make the contracty part last, since it&amp;rsquo;s least
important:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;func Print(s []interface{}:T) {
for _, v := range s {
fmt.Println(v)
}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So here&amp;rsquo;s the change in a nutshell. You use a real interface to define the type
of the argument. In this case it&amp;rsquo;s interface{}. This cuts out the need to
define a contract separately when we already have a way of defining an abstract
type with capabilities. The : tells the compiler that this is a parameterized
type, and T is the name given that type (though it&amp;rsquo;s not used anywhere).&lt;/p&gt;
&lt;h2 id="more-complex-types"&gt;More Complex Types&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;added this section to help remove some confusion people had with the proposal&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;More complex functions with multiple contract types are just as easily done:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;func Map(vals []interface{}:X, f func(x X) interface{}:Y) []Y {
ret := make([]Y, len(vals))
for i := range vals {
ret[i] = f(vals[i])
}
return ret
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;:X defines a type in this scope which is constrained by the interface that
precedes it (in this case, there&amp;rsquo;s no constraint). Y defines a separate type&amp;hellip;
then inside the scope you can reference those types.&lt;/p&gt;
&lt;h2 id="contract-definitions-as-code-are-hard"&gt;Contract Definitions as Code Are Hard&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Specifying contracts via example code is going to age about as well as
specifying time formats via example output. -me on Twitter&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The next example in the design is&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;contract stringer(x T) {
var s string = x.String()
}
func Stringify(type T stringer)(s []T) (ret []string) {
for _, v := range s {
ret = append(ret, v.String())
}
return ret
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wait, so we have to redefine the Stringer interface? Why? WHy not just &lt;em&gt;use&lt;/em&gt; a
Stringer interface? Also, what happens if I screw up the code, like this?&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;contract stringer(x T) {
s := x.String()
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You think the error message from that is going to be good? I don&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;Also, this allows an arbitrarily large amount of code in contract definitions.
Much of this code could easily imply restrictions that you don&amp;rsquo;t intend, or be
more general than you expect.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;contract slicer(x T) {
s := x[0]
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Is that a map of int to something? Or is it a slice? Is that just invalid? What
would the error message say, if so? Would it change if I put a 1 in the index?
Or -1? Or &amp;ldquo;1&amp;rdquo;?&lt;/p&gt;
&lt;p&gt;Notably&amp;hellip; a lot of really smart gophers who have been programming in Go for
years have difficulty defining contracts that are conceptually simple, because
there is so much implied functionality in even simple types.&lt;/p&gt;
&lt;p&gt;Take a contract that says you can accept a string or a []byte&amp;hellip; what do you
think it would look like?&lt;/p&gt;
&lt;p&gt;If you guessed this with even your second or third try&amp;hellip;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;contract stringOrBytes(s S) {
string(s)
s[0]
s[:]
S([]byte{})
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&amp;hellip;then I applaud you for being better at Go than I am. And there&amp;rsquo;s still
questions about whether or not this would fail for len(s) == 0 (answer: it
won&amp;rsquo;t, because it&amp;rsquo;s just type checked, not actually run&amp;hellip; but, see what I mean
about implications?) Also, I&amp;rsquo;m not even 100% sure this is sufficient to define
everything you need. It doesn&amp;rsquo;t seem to say that you can range over the type.
It doesn&amp;rsquo;t say that indexing the value will produce a single byte.&lt;/p&gt;
&lt;h2 id="lack-of-names-and-documentations"&gt;Lack of Names and Documentations&lt;/h2&gt;
&lt;p&gt;The biggest problem with contracts defined as random blocks of code is their
lack of documentation. As above, what exactly a bit of code means in a contract
is actually quite hard to distill when you&amp;rsquo;re talking about generic types. And
then how do you talk about it? If you have your function that takes your
locally defined stringOrByte, and someone else has theirs defined as robytes,
but the contents are the same (but maybe in a different order with different
type names)&amp;hellip; how can you figure out if they&amp;rsquo;re compatible?&lt;/p&gt;
&lt;p&gt;Is this the same contract as above?&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;contract robytes(t T) {
T([]byte{})
t[5:10]
string(t)
t[100]
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yes, but it&amp;rsquo;s non-trivial to see that it is (and if it wasn&amp;rsquo;t, you&amp;rsquo;d probably
have to rely on the compiler to tell you).&lt;/p&gt;
&lt;p&gt;Imagine for a moment if there were no io.Reader or io.Writer interfaces. How
would you talk about functions that write to a slice of bytes? Would we all
write exactly the same interface? Probably not. Look at the lack of a Logging
interface, and how that affected logging across the ecosystem. io.Reader and
io.Writer make writing and reading streams of bytes so nice &lt;em&gt;because&lt;/em&gt; they&amp;rsquo;re
standardized, &lt;em&gt;because&lt;/em&gt; they are discoverable. The standardization means that
everyone who writes streams of bytes uses the exact same signature, so we can
compose readers and writers trivially, and discover new ways to compose them
just by looking for the terms io.Reader and io.Writer.&lt;/p&gt;
&lt;h2 id="just-use-interfaces-and-make-some-new-built-in-ones"&gt;Just Use Interfaces, and Make Some New Built-in Ones&lt;/h2&gt;
&lt;p&gt;My solution is to mainly just use interfaces and tag them with :T to denote
they&amp;rsquo;re a parameterized type. For contracts that don&amp;rsquo;t distill to &amp;ldquo;has a
method&amp;rdquo;, make built-in contract/interfaces that can be well-documented and
well-known. Most of the examples I&amp;rsquo;ve seen of &amp;ldquo;But how would you do X?&amp;rdquo; boil
down to &amp;ldquo;You can&amp;rsquo;t, and moreover, you probably shouldn&amp;rsquo;t&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;A lot of this boils down to &amp;ldquo;I trust the stdlib authors to define a good set of
contracts and I don&amp;rsquo;t want every random coder to throw a bunch of code in a
contract block and expect me to be able to understand it&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I think most of the useful contracts can be defined in a small finite list that
can live in a new stdlib package, maybe called ct to keep it brief.
ct.Comparable could mean x == x. ct.Stringish could mean &amp;ldquo;string or []byte or a
named version of either&amp;rdquo;&amp;hellip; etc.&lt;/p&gt;
&lt;p&gt;Most of the things that fall outside of this are things that I don&amp;rsquo;t think you
should be doing. Like, &amp;ldquo;How do you make a function that can compare two
different types with ==?&amp;rdquo; Uh&amp;hellip; don&amp;rsquo;t, that&amp;rsquo;s a bad idea.&lt;/p&gt;
&lt;p&gt;One of the uses in the contract design is a way to say that you can convert one
thing to another. This can be useful for generic functions on strings vs []byte
or int vs int64. This could be yet another specialized interface:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;package ct
// Convertible defines a type that can be converted into T.
type Convertible:T contract
// elsewhere
func ParseUint64(v ct.Convertible:uint64) {
i, err := strconv.ParseUint(uint64(v))
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The contracts design, as written, IMO, will make the language significantly
worse. Wrapping my head around what a random contract actually means for my
code is just too hard if we&amp;rsquo;re using example code as the means of definition.
Sure, it&amp;rsquo;s a clever way to ensure that only types that can be used in that way
are viable&amp;hellip; but clever isn&amp;rsquo;t good.&lt;/p&gt;
&lt;p&gt;One of my favorite posts about Go is Rob Napier&amp;rsquo;s &lt;a href="http://robnapier.net/go-is-a-shop-built-jig"&gt;Go is a Shop-Built
Jig&lt;/a&gt;. In it, he argues that there
are many ineleagant parts to the Go language, but that they exist to make the
whole work better for actual users. This is stuff like the built-in functions
append and copy, the fact that slices and maps are generic, but nothing else is.
Little pieces are filed off here, stapled on there, because making usage easy
matters more than looking slick.&lt;/p&gt;
&lt;p&gt;This design of contracts as written does not feel like a shop-built jig. It
feels like a combination all-in-one machine that can do anything but is so
complicated that you don&amp;rsquo;t even know how to even approach it or when you should
use it vs the other tools in your shop.&lt;/p&gt;
&lt;p&gt;I think we can make a smaller, more incremental addition to the language that
will fix a lot of the problems that many people have with Go - lack of reusable
container types, copy and paste for simple map and filter functions, etc. This
will only add a small amount of complexity to the language, while solving real
problems that people experience.&lt;/p&gt;
&lt;p&gt;Notably, I think a lot of the problems generics solve are actually quite minor
in the scheme of major projects. Yes, I have to rewrite a filter function for
every type. But that&amp;rsquo;s a function I could have written in college and I usually
only need one or two per 20,000 lines of code (and then almost always just
strings).&lt;/p&gt;
&lt;p&gt;So&amp;hellip; I really don&amp;rsquo;t want to add a bunch of complexity to solve these problems.
Let&amp;rsquo;s take the most straightforward fix we can get, with the least impact on the
language. Go has been an amazing success in the last decade. Let&amp;rsquo;s move slowly
so we don&amp;rsquo;t screw that up in the next decade.&lt;/p&gt;</description></item><item><title>Comment Your Code</title><link>https://npf.io/2017/11/comments/</link><pubDate>Fri, 17 Nov 2017 14:48:09 -0500</pubDate><guid>https://npf.io/2017/11/comments/</guid><description>&lt;p&gt;There&amp;rsquo;s a disturbing thread that pops up every once in a while where People On
The Internet say that comments are bad and the only reason you need them is
because you and/or your code aren&amp;rsquo;t good enough. I&amp;rsquo;m here to say that&amp;rsquo;s bullshit.&lt;/p&gt;
&lt;h2 id="code-sucks"&gt;Code Sucks&lt;/h2&gt;
&lt;p&gt;They&amp;rsquo;re not entirely wrong&amp;hellip; your code isn&amp;rsquo;t good enough. Neither is mine or
anyone else&amp;rsquo;s. Code sucks. You know when it sucks the most? When you haven&amp;rsquo;t
touched it in 6 months. And you look back at the code and wonder &amp;ldquo;what in the
hell was the author thinking?&amp;rdquo; (and then you git blame and it&amp;rsquo;s you&amp;hellip; because
it&amp;rsquo;s always you).&lt;/p&gt;
&lt;p&gt;The premise of the anti-commenters is that the only reason you need comments is
because your code isn&amp;rsquo;t &amp;ldquo;clean&amp;rdquo; enough. If it were refactored better, named
better, written better, it wouldn&amp;rsquo;t need that comment.&lt;/p&gt;
&lt;p&gt;But of course, what is clean and obvious and well-written to you, today, while
the entire project and problem space are fully loaded in your brain&amp;hellip; might not
be obvious to you, six months from now, or to the poor schmuck that has to debug
your code with their manager breathing down their neck because the CTO just ran
into a critical bug in prod.&lt;/p&gt;
&lt;p&gt;Learning to look at a piece of code that you understand, and trying to figure out
how someone else might fail to understand it is a difficult skill to master. But
it is incredibly valuable&amp;hellip; one that is nearly as important as the
ability to write good code in the first place. In industry, almost no one codes
alone. And even if you &lt;em&gt;do&lt;/em&gt; code alone, you&amp;rsquo;re gonna forget why you wrote some
of your code, or what exactly this gnarly piece of late night &amp;ldquo;engineering&amp;rdquo; is
doing. And someday you&amp;rsquo;re going to leave, and the person they hire to replace
you is going to have to figure out every little quirk that was in your head at
the time.&lt;/p&gt;
&lt;p&gt;So, throwing in comments that may seem overly obvious in the moment is not a bad
thing. Sometimes it can be a huge help.&lt;/p&gt;
&lt;h2 id="avoiding-comments-often-makes-your-code-worse"&gt;Avoiding Comments Often Makes Your Code Worse&lt;/h2&gt;
&lt;p&gt;Some people claim that if you remove comments, it makes your code better,
because you have to make your code clearer to compensate. I call BS on this as
well, because I don&amp;rsquo;t think anyone is realistically writing sub-par code and
then excusing it by slapping a comment on it (aside from &lt;code&gt;// TODO: this is a temporary hack, I'll fix it later&lt;/code&gt;). We all write the best code we know how,
given the various external constraints (usually time).&lt;/p&gt;
&lt;p&gt;The problem with refactoring your code to avoid needing comments is that
it often leads to &lt;em&gt;worse&lt;/em&gt; code, not better. The canonical example is factoring
out a complicated line of code into a function with a descriptive name. Which
sounds good, except now you&amp;rsquo;ve introduced a context switch for the person reading
the code.. instead of the actual line of code, they have a function call&amp;hellip; they
have to scroll to where the function call is, remember and map the arguments
from the call site to the function declaration, and then map the return value
back to the call site&amp;rsquo;s return.&lt;/p&gt;
&lt;p&gt;In addition, the clarity of a function&amp;rsquo;s name is only applicable to very trivial
comments. Any comment that is more than a couple words cannot (or should not)
be made into a function name. Thus, you end up with&amp;hellip; a function with a
comment above it.&lt;/p&gt;
&lt;p&gt;Indeed, even the existence of a very short function may cause confusion and more
complicated code. If I see such a function, I may search to see where else that
function is used. If it&amp;rsquo;s only used in one place, I then have to wonder if this
is actually a general piece of code that represents global logic&amp;hellip; (e.g.
&lt;code&gt;NameToUserID&lt;/code&gt;) or if this function is bespoke code that relies heavily on the
specific state and implementation of its call site and may well not do the right
thing elsewhere. By breaking it out into a function, you&amp;rsquo;re in essence exposing
this implementation detail to the rest of the codebase, and this is not a
decision that should be taken lightly. Even if you know that this is not
actually a function anyone else should call, someone else &lt;em&gt;will&lt;/em&gt; call it at some
point, even where not appropriate.&lt;/p&gt;
&lt;p&gt;The problems with small functions are better detailed in Cindy Sridharan&amp;rsquo;s &lt;a href="https://medium.com/@copyconstruct/small-functions-considered-harmful-91035d316c29"&gt;medium post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We could dive into long variable names vs. short, but I&amp;rsquo;ll stop and just
say that you can&amp;rsquo;t save yourself by making variable names longer. Unless your
variable name is the entire comment that you&amp;rsquo;re avoiding writing, then you&amp;rsquo;re
still losing information that could have been added to the comment. And I think
we can all agee that &lt;code&gt;usernameStrippedOfSpacesWithDotCSVExtension&lt;/code&gt; is a terrible
variable name.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not trying to say that you shouldn&amp;rsquo;t strive to make your code clear and
obvious. You definitely should. It&amp;rsquo;s the hallmark of a good developer. But
code clarity is orthogonal to the existence of comments. And good comments are
&lt;em&gt;also&lt;/em&gt; the hallmark of a good developer.&lt;/p&gt;
&lt;h2 id="there-are-no-bad-comments"&gt;There are no bad comments&lt;/h2&gt;
&lt;p&gt;The examples of bad comments often given in these discussions are trivially
bad, and almost never encountered in code written outside of a programming 101
class.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// instantiate an error
var err error
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yes, clearly, this is not a useful comment. But at the same time, it&amp;rsquo;s not
really &lt;em&gt;harmful&lt;/em&gt;. It&amp;rsquo;s some noise that is easily ignored when browsing the
code. I would rather see a hundred of the above comments if it means the dev
leaves in one useful comment that saves me hours of head banging on keyboard.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m pretty sure I&amp;rsquo;ve never read any code and said &amp;ldquo;man, this code would be so
much easier to understand if it weren&amp;rsquo;t for all these comments.&amp;rdquo; It&amp;rsquo;s nearly
100% the opposite.&lt;/p&gt;
&lt;p&gt;In fact, I&amp;rsquo;ll even call out some code that I think is egregious in its lack of
comments - the Go standard library. While the code may be very correct and well
structured.. in many cases, if you don&amp;rsquo;t have a deep understanding of what the
code is doing &lt;em&gt;before&lt;/em&gt; you look at the it, it can be a challenge to understand
why it&amp;rsquo;s doing what it&amp;rsquo;s doing. A sprinkling of comments about what the logic
is doing and why would make a lot of the go standard library a lot easier to
read. In this I am specifically talking about comments inside the
implementation, not doc comments on exported functions in general (those are
generally pretty good).&lt;/p&gt;
&lt;h2 id="any-comment-is-better-than-no-comment"&gt;Any comment is better than no comment&lt;/h2&gt;
&lt;p&gt;Another chestnut the anti-commenters like to bring out is the wisdom can be
illustrated with a pithy image:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://npf.io/comments.jpg" width="200"&gt;
&lt;/figure&gt;
&lt;p&gt;Ah, hilarious, someone updated the contents and didn&amp;rsquo;t update the comment.&lt;/p&gt;
&lt;p&gt;But, that was a problem 20 years ago, when code reviews were not (generally) a
thing. But they are a thing now. And if checking that comments match the
implementation isn&amp;rsquo;t part of your code review process, then you should probably
review your code review process.&lt;/p&gt;
&lt;p&gt;Which is not to say that mistakes can&amp;rsquo;t be made&amp;hellip; in fact I filed a &amp;ldquo;comment
doesn&amp;rsquo;t match implementation&amp;rdquo; bug just yesterday. The saying goes something
like &amp;ldquo;no comment is better than an incorrect comment&amp;rdquo; which sounds obviously
true, except when you realize that if there is no comment, then devs will just
&lt;em&gt;guess&lt;/em&gt; what the code does, and probably be wrong more often than a comment would
be wrong.&lt;/p&gt;
&lt;p&gt;Even if this &lt;em&gt;does&lt;/em&gt; happen, and the code has changed, you still have valuable
information about what the code used to do. Chances are, the code still does
basically the same thing, just slightly differently. In this world of
versioning and backwards compatbility, how often does the same function get
drastically changed in functionality while maintaining the same name and
signature? Probably not often.&lt;/p&gt;
&lt;p&gt;Take the bug I filed yesterday&amp;hellip; the place where we were using the function was
calling &lt;code&gt;client.SetKeepAlive(60)&lt;/code&gt;. The comment on SetKeepAlive was
&amp;ldquo;SetKeepAlive will set the amount of time (in seconds) that the client should
wait before sending a PING request&amp;rdquo;. Cool, right? Except I noticed that
SetKeepAlive takes a time.Duration. Without any other units specified for the
value of 60, Go&amp;rsquo;s duration type defaults to&amp;hellip;. nanoseconds. Oops. Someone had
updated the function to take a Duration rather than an Int. Interestingly, it
&lt;em&gt;did&lt;/em&gt; still round the duration down to the nearest second, so the comment was
not incorrect per se, it was just misleading.&lt;/p&gt;
&lt;h2 id="why"&gt;Why?&lt;/h2&gt;
&lt;p&gt;The most important comments are the &lt;em&gt;why&lt;/em&gt; comments. Why is the code doing what
it&amp;rsquo;s doing? Why must the ID be less than 24 characters? Why are we hiding this
option on Linux? etc. The reason these are important is that you can&amp;rsquo;t figure
out the why by looking at the code. They document lessons learned by the devs,
outside constraints imposed by the business, other systems, etc. These comments
are invaluable, and almost impossible to capture in other ways (e.g. function
names should document what the function does, not why).&lt;/p&gt;
&lt;p&gt;Comments that document &lt;em&gt;what&lt;/em&gt; the code is doing are less useful, because you can
generally always figure out what the code is doing, given enough time and
effort. The code tells you what it is doing, by definition. Which is not to
say that you should never write &lt;em&gt;what&lt;/em&gt; comments. Definitely strive to write the
clearest code you can, but comments are free, so if you think someone might
misunderstand some code or otherwise have difficulty knowing what&amp;rsquo;s going on,
throw in a comment. At least, it may save them a half hour of puzzling through
your code, at best it may save them from changing it or using it in incorrect
ways that cause bugs.&lt;/p&gt;
&lt;h2 id="tests"&gt;Tests&lt;/h2&gt;
&lt;p&gt;Some people think that tests serve as documentation for functions. And, in a
way, this is true. But they&amp;rsquo;re generally very low on my list of effective
documentation. Why? Well, because they have to be incredibly precise, and thus
they are verbose, and cover a narrow strip of functionality. Every test tests
exactly one specific input and one specific output. For anything other than the
most simple function, you probably need a bunch of code to set up the inputs and
construct the outputs.&lt;/p&gt;
&lt;p&gt;For much of programming, it&amp;rsquo;s easier to describe briefly what a function does
than to write code to test what it does. Often times my tests will be multiple
times as many lines of code as the function itself&amp;hellip; whereas the doc comment on
it may only be a few sentences.&lt;/p&gt;
&lt;p&gt;In addition, tests only explain the &lt;em&gt;what&lt;/em&gt; of a function. What is it supposed to
do? They don&amp;rsquo;t explain why, and why is often more important, as stated above.&lt;/p&gt;
&lt;p&gt;You should definitely test your code, and tests can be useful in figuring out
the expected behavior of code in some edge cases&amp;hellip; but if I have to read tests
to understand your code in general, then that&amp;rsquo;s red flag that you really need to
write more/better comments.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I feel like the line between what&amp;rsquo;s a useful comment and what&amp;rsquo;s not is difficult
to find (outside of trivial examples), so I&amp;rsquo;d rather people err on the
side of writing too many comments. You never know who may be reading your code
next, so do them the favor you wish was done for you&amp;hellip; write a bunch of
comments. Keep writing comments until it feels like too many, then write a few
more. That&amp;rsquo;s probably about the right amount.&lt;/p&gt;</description></item><item><title>Code Must Never Lie</title><link>https://npf.io/2017/08/lies/</link><pubDate>Tue, 29 Aug 2017 12:40:19 -0400</pubDate><guid>https://npf.io/2017/08/lies/</guid><description>&lt;blockquote&gt;
&lt;p&gt;If you tell the truth, you don’t have to remember anything.&lt;/p&gt;
&lt;p&gt;—Mark Twain&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In a code review recently, I asked the author to change some of their asserts to
requires. Functions in testify&amp;rsquo;s assert package allow the test to continue,
whereas those in the require package end the test immediately. Thus, you use
require to avoid trying to continue running a test when we know it&amp;rsquo;ll be in a
bad state. (side note: don&amp;rsquo;t use an assert package, but that&amp;rsquo;s another post)
Since testify&amp;rsquo;s assert and require packages have the same interface, the
author&amp;rsquo;s solution was to simply change the import thusly:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;import (
assert &amp;#34;github.com/stretchr/testify/require&amp;#34;
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Bam, now all the assert.Foo calls would stop the test immediately, and we didn&amp;rsquo;t
need a big changelist changing every use of assert to require. All good,
right?&lt;/p&gt;
&lt;p&gt;No.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hell No.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Why? Because it makes the code lie. Anyone familiar with the testify package
understands the difference between assert and require. But we&amp;rsquo;ve now made code
that &lt;em&gt;looks like&lt;/em&gt; an assert, but is actually a require. People who are 200
lines down in a test file may well not realize that those asserts are actually
requires. They&amp;rsquo;ll assume the test function will continue processing after an
assert fails. They&amp;rsquo;ll be wrong, and they could accidentally write incorrect
tests because of it - tests that fail with confusing error messages.&lt;/p&gt;
&lt;p&gt;This is true in general - &lt;strong&gt;code must never lie&lt;/strong&gt;. This is a cardinal sin
amongst programmers. This is an extension of the mantra that code should be
written to be read. If code looks like it&amp;rsquo;s doing one thing when it&amp;rsquo;s actually
doing something else, someone down the road will read that code and
misunderstand it, and use it or alter it in a way that causes bugs. If they&amp;rsquo;re
lucky, the bugs will be immediate and obvious. If they&amp;rsquo;re unlucky, they&amp;rsquo;ll be
subtle and only be figured out after a long debugging session and much head
banging on keyboard. That someone might be you, even if it was your code in
the first place.&lt;/p&gt;
&lt;p&gt;If, for some reason, you have to make code that lies (to fulfill an interface or
some such), document the hell out of it. Giant yelling comments that can&amp;rsquo;t be
missed during a 2am debugging session. Because chances are, that&amp;rsquo;s when you&amp;rsquo;re
going to look at this code next, and you might forget that saveToMemory()
function actually saves to a database in AWS&amp;rsquo;s Antarctica region.&lt;/p&gt;
&lt;p&gt;So, don&amp;rsquo;t lie. Furthermore, try not to even mislead. Humans make assumptions
all the time, it&amp;rsquo;s built into how we perceive the world. As a coder, it&amp;rsquo;s your
job to anticipate what assumptions a reader may have, and ensure that they are
not incorrect, or if they are, do your best to disabuse them of their incorrect
assumptions.&lt;/p&gt;
&lt;p&gt;If possible, don&amp;rsquo;t resort to comments to inform the reader, but instead,
structure the code itself in such a way as to indicate it&amp;rsquo;s not going to behave
the way one might expect. For example, if your type has a &lt;code&gt;Write(b []byte) (int, error)&lt;/code&gt; method that is not compatible with io.Writer, consider calling it
something other than Write&amp;hellip; because everyone seeing &lt;code&gt;foo.Write&lt;/code&gt; is going to
assume that function will work like an io.Write. Instead maybe call it WriteOut
or PrintOut or anything but Write.&lt;/p&gt;
&lt;p&gt;Misleading code can be even more subtle than this. In a recent code review, the
author wrapped a single DB update in a transaction. This set off
alarm bells for me as a reviewer. As a reader, I assumed that the code must be
saving related data in multiple tables, and that&amp;rsquo;s why a transaction was needed.
Turned out, the code didn&amp;rsquo;t actually need the transaction, it was just written
that way to be consistent with some other code we had. Unfortunately, in this
case, being consistent was actually confusing&amp;hellip; because it caused the reader to
make assumptions that were ultimately incorrect.&lt;/p&gt;
&lt;p&gt;Do the poor sap that has to maintain your code 6 months or two years down the
road a favor - don&amp;rsquo;t lie. Try not to mislead. Because even if that poor sap
isn&amp;rsquo;t you, they still don&amp;rsquo;t deserve the 2am headache you&amp;rsquo;ll likely be
inflicting.&lt;/p&gt;</description></item></channel></rss>