diff --git a/public/2010/01.html b/public/2010/01.html new file mode 100644 index 0000000..f6f79cd --- /dev/null +++ b/public/2010/01.html @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + +January 2010 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Introduction

+ + + +
+

Welcome to the BoochTek blog. BoochTek, LLC is a small web development company, based in St. Louis, Missouri. We specialize in Ruby on Rails, jQuery (JavaScript), and PHP.

+

This bog will be a place where we can share some of our experiences in web development, Open Source, programming, and technology in general.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2010/01/01/introduction.html b/public/2010/01/01/introduction.html new file mode 100644 index 0000000..f0b6a20 --- /dev/null +++ b/public/2010/01/01/introduction.html @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + +Introduction – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Introduction

+ + + +
+

Welcome to the BoochTek blog. BoochTek, LLC is a small web development company, based in St. Louis, Missouri. We specialize in Ruby on Rails, jQuery (JavaScript), and PHP.

+

This bog will be a place where we can share some of our experiences in web development, Open Source, programming, and technology in general.

+
+ + +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2011/08.html b/public/2011/08.html new file mode 100644 index 0000000..22e93d2 --- /dev/null +++ b/public/2011/08.html @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + +August 2011 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Bulk Rename in Bash

+ + + +
+

Here’s a relatively simple way to rename a bunch of files from the command line. It uses sed within a command substitution to compute the new names from the old names.

+

In this example, we’re renaming files that start with “ABC” to start with “XYZ” instead:

+
  for i in ABC*; do mv $i $(echo $i | sed -e s/^ABC/XYZ/); done
+

You’ll have to use shell globbing (wildcards) in the first part, to determine which files will be the source of the renaming, and regular expressions in the second part to translate the old names into the new names.

+

It’s a good idea to use echo in place of mv before running this, to see what the results will be, so you don’t make a mistake.

+

Depending on the situation, you may need to adapt this for your particular needs. For example, if you have too many files to rename, you would need to use xargs. Or sed might not be up for the task of transforming the names that you want. Or you might need to use find to find files within a hierarchy. As with any UNIX idiom, there are hundreds of variations on the theme.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2011/08/26/bulk-rename-in-bash.html b/public/2011/08/26/bulk-rename-in-bash.html new file mode 100644 index 0000000..878443f --- /dev/null +++ b/public/2011/08/26/bulk-rename-in-bash.html @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + +Bulk Rename in Bash – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Bulk Rename in Bash

+ + + +
+

Here’s a relatively simple way to rename a bunch of files from the command line. It uses sed within a command substitution to compute the new names from the old names.

+

In this example, we’re renaming files that start with “ABC” to start with “XYZ” instead:

+
  for i in ABC*; do mv $i $(echo $i | sed -e s/^ABC/XYZ/); done
+

You’ll have to use shell globbing (wildcards) in the first part, to determine which files will be the source of the renaming, and regular expressions in the second part to translate the old names into the new names.

+

It’s a good idea to use echo in place of mv before running this, to see what the results will be, so you don’t make a mistake.

+

Depending on the situation, you may need to adapt this for your particular needs. For example, if you have too many files to rename, you would need to use xargs. Or sed might not be up for the task of transforming the names that you want. Or you might need to use find to find files within a hierarchy. As with any UNIX idiom, there are hundreds of variations on the theme.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2012/01.html b/public/2012/01.html new file mode 100644 index 0000000..7719d1f --- /dev/null +++ b/public/2012/01.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + +January 2012 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Debugging Pattern – Grenade

+ + + +
+

I’ve been more interested in programming patterns lately, partly due to the Ruby community’s recent interest — especially the Ruby Rogue podcast’s love affair with “Smalltalk Best Practice Patterns”.

+

I consider most of the patterns in “Smalltalk Best Practice Patterns” to be patterns “in the small” — things that are typically employed on a single line or method. The Gang Of Four patterns are more medium sized, dealing with methods and classes. The PEAA book covers architectural-scale patterns. I suppose “The Pragmatic Programmer” and similar books could (should!) be considered to be very general patterns, mostly about the practice of programming.

+

One type of pattern that I have not seen discussed much is debugging patterns. I’m not exactly sure why that is; probably just our general concentration on designing the program code itself. There are definitely testing patterns. But I can’t think of anything that I’ve seen that covers debugging patterns. A quick search on the Internet doesn’t turn up too much.

+

Anyway, a co-worker (Helena) and I were debugging recently, and were having trouble figuring out where a certain method on a certain object was being called. We came up with a really cool solution. We immediately called it a grenade, because its entire purpose is to blow up (raise an exception) and display a backtrace to show us the caller of the method that we’re looking for.

+

Here’s our implementation in Ruby:

+

+module Grenade
+  def method_under_investigation(*)
+    raise "method_under_investigation called"
+  end
+end
+
+class Xyz
+  ...
+  def xyz
+    object_under_investigation.extend(Grenade)
+  end
+  ...
+end
+

I’m sure we could wrap that in some more semantic sugar, even going as far as making it look something like this:

+

+class Xyz
+  ...
+  def xyz
+    object_under_investigation.blow_up_when_method_called(:method_under_investigation)
+  end
+  ...
+end
+
+

I’m not sure that would really be worth it though, unless we were to add it to some sort of library.

+

So that’s our contribution to the (small) list of debugging patterns.

+
+ + +
+ +
+
+ +

Write Comments For Yourself

+ + + +
+

Amos and I got in a heated discussion recently on whether we should write a single line comment to better explain some code. (The code in question was Amos’s very elegant solution to testing whether a job got sent to Resque.)

+

Amos doesn’t believe in writing comments much at all. He thinks that if you’re writing a comment, it means that you’re doing something wrong, and that you probably need to write the code more clearly.

+

I agree with that, to a point. First off, it’s not necessary to write perfect code. If you can change a class or method name to better describe what you’re doing (and more importantly, why you’re doing it) then you should definitely do so. But it’s not always worth refactoring until you get every “why” answered. More importantly, I don’t think it’s even possible to capture everything in the code that is worth capturing. For example, why did you choose this implementation, as opposed to another that might be more obvious or common?

+

After our argument, I came up with a good rule of thumb (or “pattern”):

+

Write comments for your (future) self.1

+

In other words, if your comment will help you to understand the code more quickly when you look at it in the future, then it’s a valid comment. It also means that you can assume that the reader has about as much general programming knowledge as you currently do. (Your future self will have more general knowledge, but possibly less specific knowledge of the lines of code in question. And because of this, your current solution might not make as much sense in the future. You might know of a better solution in the future, but you’ll have to know all the constraints that you had when you originally wrote the code.)

+

This is not to say that you should not write comments in clear English, that others can understand. The comment is written for a future maintainer. That may be you (which is why the rule works well), or it may be someone else. The rule is more about when to write a comment, and what level of competence you should assume of the reader.

+

1 Perhaps it should be “Write comments TO your (future) self”.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2012/01/10/write-comments-for-yourself-2.html b/public/2012/01/10/write-comments-for-yourself-2.html new file mode 100644 index 0000000..6e432fe --- /dev/null +++ b/public/2012/01/10/write-comments-for-yourself-2.html @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + +Write Comments For Yourself – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Write Comments For Yourself

+ + + +
+

Amos and I got in a heated discussion recently on whether we should write a single line comment to better explain some code. (The code in question was Amos’s very elegant solution to testing whether a job got sent to Resque.)

+

Amos doesn’t believe in writing comments much at all. He thinks that if you’re writing a comment, it means that you’re doing something wrong, and that you probably need to write the code more clearly.

+

I agree with that, to a point. First off, it’s not necessary to write perfect code. If you can change a class or method name to better describe what you’re doing (and more importantly, why you’re doing it) then you should definitely do so. But it’s not always worth refactoring until you get every “why” answered. More importantly, I don’t think it’s even possible to capture everything in the code that is worth capturing. For example, why did you choose this implementation, as opposed to another that might be more obvious or common?

+

After our argument, I came up with a good rule of thumb (or “pattern”):

+

Write comments for your (future) self.1

+

In other words, if your comment will help you to understand the code more quickly when you look at it in the future, then it’s a valid comment. It also means that you can assume that the reader has about as much general programming knowledge as you currently do. (Your future self will have more general knowledge, but possibly less specific knowledge of the lines of code in question. And because of this, your current solution might not make as much sense in the future. You might know of a better solution in the future, but you’ll have to know all the constraints that you had when you originally wrote the code.)

+

This is not to say that you should not write comments in clear English, that others can understand. The comment is written for a future maintainer. That may be you (which is why the rule works well), or it may be someone else. The rule is more about when to write a comment, and what level of competence you should assume of the reader.

+

1 Perhaps it should be “Write comments TO your (future) self”.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2012/01/11/grenade-debugging-pattern.html b/public/2012/01/11/grenade-debugging-pattern.html new file mode 100644 index 0000000..647b0d5 --- /dev/null +++ b/public/2012/01/11/grenade-debugging-pattern.html @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + +Debugging Pattern – Grenade – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Debugging Pattern – Grenade

+ + + +
+

I’ve been more interested in programming patterns lately, partly due to the Ruby community’s recent interest — especially the Ruby Rogue podcast’s love affair with “Smalltalk Best Practice Patterns”.

+

I consider most of the patterns in “Smalltalk Best Practice Patterns” to be patterns “in the small” — things that are typically employed on a single line or method. The Gang Of Four patterns are more medium sized, dealing with methods and classes. The PEAA book covers architectural-scale patterns. I suppose “The Pragmatic Programmer” and similar books could (should!) be considered to be very general patterns, mostly about the practice of programming.

+

One type of pattern that I have not seen discussed much is debugging patterns. I’m not exactly sure why that is; probably just our general concentration on designing the program code itself. There are definitely testing patterns. But I can’t think of anything that I’ve seen that covers debugging patterns. A quick search on the Internet doesn’t turn up too much.

+

Anyway, a co-worker (Helena) and I were debugging recently, and were having trouble figuring out where a certain method on a certain object was being called. We came up with a really cool solution. We immediately called it a grenade, because its entire purpose is to blow up (raise an exception) and display a backtrace to show us the caller of the method that we’re looking for.

+

Here’s our implementation in Ruby:

+

+module Grenade
+  def method_under_investigation(*)
+    raise "method_under_investigation called"
+  end
+end
+
+class Xyz
+  ...
+  def xyz
+    object_under_investigation.extend(Grenade)
+  end
+  ...
+end
+

I’m sure we could wrap that in some more semantic sugar, even going as far as making it look something like this:

+

+class Xyz
+  ...
+  def xyz
+    object_under_investigation.blow_up_when_method_called(:method_under_investigation)
+  end
+  ...
+end
+
+

I’m not sure that would really be worth it though, unless we were to add it to some sort of library.

+

So that’s our contribution to the (small) list of debugging patterns.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2013/02.html b/public/2013/02.html new file mode 100644 index 0000000..a67342e --- /dev/null +++ b/public/2013/02.html @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + +February 2013 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

My Thoughts on Python vs. Ruby

+ + + +
+

I’ve been using Python at work for the past few months.  I learned Python back in the early 2000s, but never used it for any large projects.  I learned Ruby in late 2005, and it quickly became my language of choice for most cases.

+

So while I still prefer Ruby, and will likely use Ruby more in the future than Python, I wanted to assess the strengths and weaknesses of Python in relation to Ruby.  Perhaps some of the lessons could be applied when writing Ruby, and it could help to decide when to use each.  Also, I’m interested in programming language design, and wanted to document pros and cons in that light.

+

Where Python Sucks (As Compared to Ruby)

+
    +
  • Have to explicitly include self in EVERY method declaration. +
      +
    • Including @classmethod declarations (although people usually use the name cls instead of self there).
    • +
    • Except @staticmethod declarations.
    • +
    +
  • +
  • Have to use self everywhere to reference an object’s own attributes.
  • +
  • Inconsistency of things like the len() function, when everything else is a method.
  • +
  • Inconsistency of having some built-in classes with lower-case names. +
      +
    • And they’re not whole words, so you have to memorize them: list, dict, str, int.
    • +
    • Even more bizarre is dict vs. OrderedDict.
    • +
    +
  • +
  • I’m not a fan of True and False being uppercase. +
      +
    • I’m a bit less concerned about None, for some reason.
    • +
    • If they’re going to be uppercase, they might as well be all caps, in my opinion.
    • +
    +
  • +
  • Inconsistency of camelCase versus snake_case in some modules.
  • +
  • Claims to prefer explicit over implicit, yet does not use new to create an object. +
      +
    • It is nicely concise, but makes it look too much like a regular function call.
    • +
    +
  • +
  • The implementation of lambdas is too limited. +
      +
    • Makes using map function not very useful.
    • +
    +
  • +
  • Class methods are less than ideal to implement. +
      +
    • Probably better to use functions within a module instead, in many cases.
    • +
    +
  • +
  • Lack of good functional programming tools makes it harder to manipulate lists. +
      +
    • Have to often resort to creating an initial list and adding to it in a for loop.
    • +
    +
  • +
  • I don’t understand why r’regex_string’ doesn’t just create an actual regular expression object.
  • +
  • I miss Ruby’s method-call-or-property-getter syntax. +
      +
    • Nice that I can get it by adding @property to method definitions, but that’s a bit messy.
    • +
    +
  • +
  • I don’t understand why lists don’t have a join() method; it seems backwards to call join on the string used to connect the list elements.
  • +
  • I miss unless; seems like with all the keywords, Python would have added that.
  • +
  • I really miss ||= to memoize. +
      +
    • The distinction between unset variables and variables set to None makes it hard.
    • +
    • I also miss +=.
    • +
    +
  • +
  • I really miss statement modifiers (if or unless at the end of the statement/expression instead of the beginning).
  • +
  • I miss being able to assign to a compound statement (x = if True: 1; else: 2). +
      +
    • While Python does allow this simple case, it does not allow more complex cases.
    • +
    +
  • +
  • The way modules, files, and classes work, I either have a lot of classes in one file, or have to come up with more module names in order to split classes into different files.
  • +
  • The preference for bare functions within modules over class methods often leads to functions outside of classes, when they’d make more sense more closely associated with the class.
  • +
  • I don’t quite understand the necessity for pass. +
      +
    • Seems like allowing 0 lines of indented code would suffice instead.
    • +
    +
  • +
  • I miss implicit return. +
      +
    • Explicit return looks a bit nicer, but is less concise, and I’ve gotten out of the habit.
    • +
    +
  • +
  • Mixins turn out to be more useful than multiple inheritance.
  • +
  • I miss string interpolation.
  • +
  • I really miss Array#first and Array#last.
  • +
  • I really don’t like that 0 is falsey. +
      +
    • I could live with empty lists and dicts being false, but not 0.0 and 0.
    • +
    +
  • +
  • Comparing a string to a regular expression seems harder than it should be.
  • +
  • Converting to a Boolean is not as easy a Ruby’s !! syntax. +
      +
    • OK, bool(expression) isn’t so bad, I guess.
    • +
    +
  • +
+

Where Python Rocks

+
    +
  • Indentation removes the need for end everywhere.
  • +
  • Import statements are nice. +
      +
    • Can import everything, or just a few things.
    • +
    +
  • +
  • Annotations are nice for aspect-oriented modification of methods. +
      +
    • Does it make it hard to debug though, like alias_method_chain in Ruby?
    • +
    • It’s kind of tricky to write them though, due to doubly-nested function definitions.
    • +
    +
  • +
  • List comprehensions are more powerful than map. +
      +
    • But map can be more concise for the most common cases.
    • +
    +
  • +
  • Can add arbitrary attributes to any object, class, or module.
  • +
  • The object creation syntax is nicely concise.
  • +
  • Sometimes the ability to add a bare function within a module is nice.
  • +
  • Keyword arguments are nicely done. +
      +
    • Ruby’s emulation via hashes without braces is OK, but the corner cases are problematic.
    • +
    +
  • +
  • Docstrings as part of the language is nice.
  • +
  • The new with keyword looks like a halfway-decent replacement for blocks. +
      +
    • Seems like a lot more work that using blocks and yield though.
    • +
    +
  • +
  • Python 3 drops support for octal literals starting with a 0. +
      +
    • Still allows it via a 0o prefix.
    • +
    +
  • +
  • No support for all the crazy Perl-inspired globals.
  • +
  • The names list and dictionary are better than array and hash. +
      +
    • The Ruby names are more about the implementation, especially hash.
    • +
    • I’d much prefer the name map over hash, or even dictionary.
    • +
    • The name list is only slightly better than array.
    • +
    +
  • +
  • List and string slicing is quite nice. +
      +
    • I do wish that the syntax was “..” instead of “:” though.
    • +
    • Slice assignment is even cooler.
    • +
    +
  • +
  • I prefer the Python dict syntax over the Ruby hash syntax (“:” vs. “=>”). +
      +
    • The Ruby 1.9 symbol hash syntax is an improvement, but not quite as good.
    • +
    +
  • +
  • Checking for string containment is nice: if substring in string.
  • +
+

Where Ruby Rocks

+
    +
  • Consistency.
  • +
  • Blocks.
  • +
  • Excellent functional tools to deal with Enumerable.
  • +
  • Meta-programming.
  • +
  • Optional parentheses.
  • +
  • Modules and classes are also objects.
  • +
+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2013/02/22/my-thoughts-on-python-vs-ruby.html b/public/2013/02/22/my-thoughts-on-python-vs-ruby.html new file mode 100644 index 0000000..be21c73 --- /dev/null +++ b/public/2013/02/22/my-thoughts-on-python-vs-ruby.html @@ -0,0 +1,471 @@ + + + + + + + + + + + + + + + + +My Thoughts on Python vs. Ruby – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

My Thoughts on Python vs. Ruby

+ + + +
+

I’ve been using Python at work for the past few months.  I learned Python back in the early 2000s, but never used it for any large projects.  I learned Ruby in late 2005, and it quickly became my language of choice for most cases.

+

So while I still prefer Ruby, and will likely use Ruby more in the future than Python, I wanted to assess the strengths and weaknesses of Python in relation to Ruby.  Perhaps some of the lessons could be applied when writing Ruby, and it could help to decide when to use each.  Also, I’m interested in programming language design, and wanted to document pros and cons in that light.

+

Where Python Sucks (As Compared to Ruby)

+
    +
  • Have to explicitly include self in EVERY method declaration. +
      +
    • Including @classmethod declarations (although people usually use the name cls instead of self there).
    • +
    • Except @staticmethod declarations.
    • +
    +
  • +
  • Have to use self everywhere to reference an object’s own attributes.
  • +
  • Inconsistency of things like the len() function, when everything else is a method.
  • +
  • Inconsistency of having some built-in classes with lower-case names. +
      +
    • And they’re not whole words, so you have to memorize them: list, dict, str, int.
    • +
    • Even more bizarre is dict vs. OrderedDict.
    • +
    +
  • +
  • I’m not a fan of True and False being uppercase. +
      +
    • I’m a bit less concerned about None, for some reason.
    • +
    • If they’re going to be uppercase, they might as well be all caps, in my opinion.
    • +
    +
  • +
  • Inconsistency of camelCase versus snake_case in some modules.
  • +
  • Claims to prefer explicit over implicit, yet does not use new to create an object. +
      +
    • It is nicely concise, but makes it look too much like a regular function call.
    • +
    +
  • +
  • The implementation of lambdas is too limited. +
      +
    • Makes using map function not very useful.
    • +
    +
  • +
  • Class methods are less than ideal to implement. +
      +
    • Probably better to use functions within a module instead, in many cases.
    • +
    +
  • +
  • Lack of good functional programming tools makes it harder to manipulate lists. +
      +
    • Have to often resort to creating an initial list and adding to it in a for loop.
    • +
    +
  • +
  • I don’t understand why r’regex_string’ doesn’t just create an actual regular expression object.
  • +
  • I miss Ruby’s method-call-or-property-getter syntax. +
      +
    • Nice that I can get it by adding @property to method definitions, but that’s a bit messy.
    • +
    +
  • +
  • I don’t understand why lists don’t have a join() method; it seems backwards to call join on the string used to connect the list elements.
  • +
  • I miss unless; seems like with all the keywords, Python would have added that.
  • +
  • I really miss ||= to memoize. +
      +
    • The distinction between unset variables and variables set to None makes it hard.
    • +
    • I also miss +=.
    • +
    +
  • +
  • I really miss statement modifiers (if or unless at the end of the statement/expression instead of the beginning).
  • +
  • I miss being able to assign to a compound statement (x = if True: 1; else: 2). +
      +
    • While Python does allow this simple case, it does not allow more complex cases.
    • +
    +
  • +
  • The way modules, files, and classes work, I either have a lot of classes in one file, or have to come up with more module names in order to split classes into different files.
  • +
  • The preference for bare functions within modules over class methods often leads to functions outside of classes, when they’d make more sense more closely associated with the class.
  • +
  • I don’t quite understand the necessity for pass. +
      +
    • Seems like allowing 0 lines of indented code would suffice instead.
    • +
    +
  • +
  • I miss implicit return. +
      +
    • Explicit return looks a bit nicer, but is less concise, and I’ve gotten out of the habit.
    • +
    +
  • +
  • Mixins turn out to be more useful than multiple inheritance.
  • +
  • I miss string interpolation.
  • +
  • I really miss Array#first and Array#last.
  • +
  • I really don’t like that 0 is falsey. +
      +
    • I could live with empty lists and dicts being false, but not 0.0 and 0.
    • +
    +
  • +
  • Comparing a string to a regular expression seems harder than it should be.
  • +
  • Converting to a Boolean is not as easy a Ruby’s !! syntax. +
      +
    • OK, bool(expression) isn’t so bad, I guess.
    • +
    +
  • +
+

Where Python Rocks

+
    +
  • Indentation removes the need for end everywhere.
  • +
  • Import statements are nice. +
      +
    • Can import everything, or just a few things.
    • +
    +
  • +
  • Annotations are nice for aspect-oriented modification of methods. +
      +
    • Does it make it hard to debug though, like alias_method_chain in Ruby?
    • +
    • It’s kind of tricky to write them though, due to doubly-nested function definitions.
    • +
    +
  • +
  • List comprehensions are more powerful than map. +
      +
    • But map can be more concise for the most common cases.
    • +
    +
  • +
  • Can add arbitrary attributes to any object, class, or module.
  • +
  • The object creation syntax is nicely concise.
  • +
  • Sometimes the ability to add a bare function within a module is nice.
  • +
  • Keyword arguments are nicely done. +
      +
    • Ruby’s emulation via hashes without braces is OK, but the corner cases are problematic.
    • +
    +
  • +
  • Docstrings as part of the language is nice.
  • +
  • The new with keyword looks like a halfway-decent replacement for blocks. +
      +
    • Seems like a lot more work that using blocks and yield though.
    • +
    +
  • +
  • Python 3 drops support for octal literals starting with a 0. +
      +
    • Still allows it via a 0o prefix.
    • +
    +
  • +
  • No support for all the crazy Perl-inspired globals.
  • +
  • The names list and dictionary are better than array and hash. +
      +
    • The Ruby names are more about the implementation, especially hash.
    • +
    • I’d much prefer the name map over hash, or even dictionary.
    • +
    • The name list is only slightly better than array.
    • +
    +
  • +
  • List and string slicing is quite nice. +
      +
    • I do wish that the syntax was “..” instead of “:” though.
    • +
    • Slice assignment is even cooler.
    • +
    +
  • +
  • I prefer the Python dict syntax over the Ruby hash syntax (“:” vs. “=>”). +
      +
    • The Ruby 1.9 symbol hash syntax is an improvement, but not quite as good.
    • +
    +
  • +
  • Checking for string containment is nice: if substring in string.
  • +
+

Where Ruby Rocks

+
    +
  • Consistency.
  • +
  • Blocks.
  • +
  • Excellent functional tools to deal with Enumerable.
  • +
  • Meta-programming.
  • +
  • Optional parentheses.
  • +
  • Modules and classes are also objects.
  • +
+

 

+
+ + +
+ +
+ +

+ 1 thought on “My Thoughts on Python vs. Ruby”

+ + +
    +
  1. +
    + + +
    +

    I feel like the lesson here is: Yes, if you come to Python expecting Ruby, you’re gonna have a bad time.

    +
    + +
    +
  2. +
+ + + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2013/02/22/my-thoughts-on-python-vs-ruby/feed b/public/2013/02/22/my-thoughts-on-python-vs-ruby/feed new file mode 100644 index 0000000..605031a --- /dev/null +++ b/public/2013/02/22/my-thoughts-on-python-vs-ruby/feed @@ -0,0 +1,18 @@ + + + Comments on: My Thoughts on Python vs. Ruby + + http://blog.boochtek.com/2013/02/22/my-thoughts-on-python-vs-ruby + Web Development, Ruby on Rails, Open Source + Sun, 26 Jan 2014 07:15:03 +0000 + hourly + 1 + http://wordpress.org/?v=3.8 + + diff --git a/public/2014/01.html b/public/2014/01.html new file mode 100644 index 0000000..31aa10c --- /dev/null +++ b/public/2014/01.html @@ -0,0 +1,387 @@ + + + + + + + + + + + + + + + +January 2014 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Testing Rails Validators

+ + + +
+

It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+
+ + +
+ +
+
+ +

Introspective

+ + + +
+

Today I was informed that I’ve been impeding progress on our team.
+This was somewhat shocking, since I feel like I’m always pushing
+forward to make our team and myself better.

+

Like most anyone, my initial reaction to criticism was defensiveness.
+I don’t handle criticism well. (You might find that hard to believe,
+after reading the rest of this post. Maybe it’s just the confrontation
+part I don’t handle well.) Thankfully the blow was softened somewhat,
+because the person providing the criticism didn’t tell me directly —
+they told Amos, a trusted colleague of mine. Amos then passed it on to
+me. I’m grateful for that — this is the best way for me to have received
+that criticism.

+

What I did next is the astonishing thing. I swallowed my pride for a
+minute. Not an easy thing for me to do, for all the usual reasons, and
+more. I decided to address the problem head on. If someone was telling
+someone else that I was causing some sort of a problem, then even that
+perception was a problem that I needed to address. So I decided to hold
+a retrospective for myself. Retrospectives are the way Agile teams address
+problems and improve how they work. If it would work for a team, I figured
+it should work for an individual.

+

I’d held retrospectives by myself before. Before I even knew about
+retrospectives, I’d taken some time to assess how I had done things
+on a couple of projects I had worked on as an independent contractor. But
+those were about technical decisions I had made and processes I had
+followed. This one would be different. This one was about a personal
+shortcoming, an action of mine that had caused some harm. This one
+involved feelings and emotions.

+

So I asked Amos to facilitate my one-person retro. We’d both done a lot
+of retrospectives, but neither one of us had done anything like this before.
+We had a bit of trouble getting started. We didn’t have any details
+about why the anonymous person felt the way he felt. So it was hard
+to address something that nebulous. So I asked Amos if he could follow
+up with the person. Luckily, the person was online and able to respond
+in real time.

+

So we talked things through some. We went on some tangents. We made some
+progress — things that I could write down as take-aways and action items.
+We got off-topic again, and then came back to it. Then Amos decided to
+play The Why Game. This worked amazingly well. It helped me to find the
+root reasons why I was holding back change. Basically I’m afraid of
+failure. Going deeper, we looked into why I’m afraid of failure. This
+line of inquiry was particularly effective. Hopefully it’ll be enough for
+me to stop being an impediment to change. Here are my notes / take-aways:

+
    +
  • Be careful playing devil’s advocate in public. +
      +
    • People will think I’m taking that stance.
    • +
    +
  • +
  • Don’t always take the boss’s side in public.
  • +
  • Don’t be the person to allow the team to take the easy path.
  • +
  • Why do I think that managers won’t let us try a 2nd time?
  • +
  • Why do I think we might fail the first time? +
      +
    • I want to put us in a better position to succeed.
    • +
    +
  • +
  • I’m being too conservative.
  • +
  • Be willing to fail. Don’t fail to try.
  • +
  • Don’t say we shouldn’t try because I think other people don’t want to try. +
      +
    • That is just self-perpetuating.
    • +
    +
  • +
  • I’m afraid of failing because I haven’t done it much. +
      +
    • Because I’m good at learning from other people’s failures.
    • +
    • But this is holding me back and making me cautious. +
        +
      • Despite the fact that I try to not be cautious.
      • +
      +
    • +
    • So I need to work to counter-act those fears.
    • +
    +
  • +
+

At first, I thought that I’d never heard of anyone doing anything like
+this. But then I recalled that I’d heard about somewhere that everyone
+does this, with their whole team. That would be hard. I’d have to
+trust everyone in the room.

+

But I would recommend this for anyone that can pull it off. It’s hard,
+but it has a large payoff. Just like a team retrospective, it feels good
+making this kind of breakthrough, and knowing that just spending that
+little amount of time has made me a better person. (Mine took about an hour.)
+I think the hardest part is finding someone (or a group of people) you
+trust to be your facilitator.

+

I’ve decided to call this thing an introspective. A retrospective is
+about looking back. This is about looking inward. I’d be interested to
+find out who is doing this kind of thing. Does it have a commonly accepted
+name? How does it work? What techniques work best? If you’ve got any answers or ideas, please comment below, or tweet me @CraigBuchek.

+

So thank you to that anonymous person. Your way of addressing this was
+probably more effective than you could have imagined that it might be.
+I hope that this exercise will help me make the changes required, to make
+me a better person, and help the team become the best that we can be.

+

Amos has written up his thoughts on the introspective.

+
+ + +
+ +
+
+ +

What’s Your Open Source Resolution?

+ + + +
+

I’ve been using GNU/Linux since 1994, starting with a Slackware 2.2 CD-ROM I ordered from the back of a magazine. I’ve been heavily involved in my local GNU/Linux community since 1999, and I give frequent presentations at various groups. I’ve made some small contributions to several Open Source projects.

+

But I don’t feel like I’ve done enough to contribute to the wider community. So this year, I’m going to resolve to step up my contributions, and do more.

+

Change my role in the LUG

+

I was lucky enough this past year to find someone interested in helping to run the St. Louis GNU/Linux Users Group (STLLUG). I’ve been running the group so long that I’ve somewhat stagnated. It’s great to find someone who will bring a fresh energy and new ideas. Thanks, Andrew!

+

Mostly freed from the more general administrative tasks (mostly planning meeting topics and speakers), I’m hoping to find the time to work on a few projects. First, I want to overhaul the web site. It needs a new look, as well as an easier way to update it. I’m planning to implement some sort of CMS — possibly WordPress or Octopress, or at the very least, a git repo to manage updates.

+

Blog regularly

+

I installed WordPress a long time ago, but I haven’t published many posts. I’ve got a few drafts in various stages. So this year I’m going to actually get the blog going, and hopefully post about once a week. I’ll be sharing things I learn on the Internet, as well as some of the things I learn from my experiences with various technologies and techniques.

+

Contribute more significant patches

+

There are a few items I’ve been meaning to work on and contribute. I’d like to finally get around to them:

+
    +
  • Make the GNU sort command be able to (easily) sort a list of IP addresses, or semantic version numbers.
  • +
  • Add MySQL-style commands to PostgreSQL: SHOW DATABASES; SHOW TABLES; DESCRIBE table. (I’m not sure how welcome these additions might be, but I think they’d make transitioning from MySQL easier for people.)
  • +
+

Publish and maintain several Ruby gems

+

I’ve been working on a few Ruby gems. I’d like to get them to a state where they’re useful to more people. These include:

+
    +
  • A way to simplify standard CRUD controller actions
  • +
  • A way to declare list views in a manner similar to SimpleForm or Formtastic
  • +
  • A way to declare ActiveRecord attributes in models using Virtus
  • +
  • A higher-level interface to ROM (Ruby Object Mapper)
  • +
  • A similar high-level way to define Perpetuity attributes in models using Virtus
  • +
  • My Rails template, making it much easier to start a Rails app
  • +
+

Build some apps

+

I enjoyed building a quick Rails app during last year’s Rails Rumble, even if it doesn’t turn out to be useful or make any money. I’d like to create a few small apps again this year, either in Rails or Apache Cordova. I’ve got some ideas — we’ll see how if they turn into anything useful or worth selling.

+

Podcast more often

+

Last year, I started joining the This Agile Life podcast. I enjoy it. I’m hoping to continue with that, and possibly doing some podcasting about Ruby and Rails.

+

Present at a conference

+

I’m going to attempt to give a presentation at a conference. I often give presentations at local groups, but haven’t addressed a larger audience. I’ve got a few ideas rattling around, and will see if I can turn them into presentations worthy of a larger conference.

+

What’s your Open Source resolution?

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2014/01/04/open-source-resolutions.html b/public/2014/01/04/open-source-resolutions.html new file mode 100644 index 0000000..89ff796 --- /dev/null +++ b/public/2014/01/04/open-source-resolutions.html @@ -0,0 +1,315 @@ + + + + + + + + + + + + + + + + +What’s Your Open Source Resolution? – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

What’s Your Open Source Resolution?

+ + + +
+

I’ve been using GNU/Linux since 1994, starting with a Slackware 2.2 CD-ROM I ordered from the back of a magazine. I’ve been heavily involved in my local GNU/Linux community since 1999, and I give frequent presentations at various groups. I’ve made some small contributions to several Open Source projects.

+

But I don’t feel like I’ve done enough to contribute to the wider community. So this year, I’m going to resolve to step up my contributions, and do more.

+

Change my role in the LUG

+

I was lucky enough this past year to find someone interested in helping to run the St. Louis GNU/Linux Users Group (STLLUG). I’ve been running the group so long that I’ve somewhat stagnated. It’s great to find someone who will bring a fresh energy and new ideas. Thanks, Andrew!

+

Mostly freed from the more general administrative tasks (mostly planning meeting topics and speakers), I’m hoping to find the time to work on a few projects. First, I want to overhaul the web site. It needs a new look, as well as an easier way to update it. I’m planning to implement some sort of CMS — possibly WordPress or Octopress, or at the very least, a git repo to manage updates.

+

Blog regularly

+

I installed WordPress a long time ago, but I haven’t published many posts. I’ve got a few drafts in various stages. So this year I’m going to actually get the blog going, and hopefully post about once a week. I’ll be sharing things I learn on the Internet, as well as some of the things I learn from my experiences with various technologies and techniques.

+

Contribute more significant patches

+

There are a few items I’ve been meaning to work on and contribute. I’d like to finally get around to them:

+
    +
  • Make the GNU sort command be able to (easily) sort a list of IP addresses, or semantic version numbers.
  • +
  • Add MySQL-style commands to PostgreSQL: SHOW DATABASES; SHOW TABLES; DESCRIBE table. (I’m not sure how welcome these additions might be, but I think they’d make transitioning from MySQL easier for people.)
  • +
+

Publish and maintain several Ruby gems

+

I’ve been working on a few Ruby gems. I’d like to get them to a state where they’re useful to more people. These include:

+
    +
  • A way to simplify standard CRUD controller actions
  • +
  • A way to declare list views in a manner similar to SimpleForm or Formtastic
  • +
  • A way to declare ActiveRecord attributes in models using Virtus
  • +
  • A higher-level interface to ROM (Ruby Object Mapper)
  • +
  • A similar high-level way to define Perpetuity attributes in models using Virtus
  • +
  • My Rails template, making it much easier to start a Rails app
  • +
+

Build some apps

+

I enjoyed building a quick Rails app during last year’s Rails Rumble, even if it doesn’t turn out to be useful or make any money. I’d like to create a few small apps again this year, either in Rails or Apache Cordova. I’ve got some ideas — we’ll see how if they turn into anything useful or worth selling.

+

Podcast more often

+

Last year, I started joining the This Agile Life podcast. I enjoy it. I’m hoping to continue with that, and possibly doing some podcasting about Ruby and Rails.

+

Present at a conference

+

I’m going to attempt to give a presentation at a conference. I often give presentations at local groups, but haven’t addressed a larger audience. I’ve got a few ideas rattling around, and will see if I can turn them into presentations worthy of a larger conference.

+

What’s your Open Source resolution?

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/01/19/introspective.html b/public/2014/01/19/introspective.html new file mode 100644 index 0000000..107b65a --- /dev/null +++ b/public/2014/01/19/introspective.html @@ -0,0 +1,394 @@ + + + + + + + + + + + + + + + + +Introspective – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Introspective

+ + + +
+

Today I was informed that I’ve been impeding progress on our team.
+This was somewhat shocking, since I feel like I’m always pushing
+forward to make our team and myself better.

+

Like most anyone, my initial reaction to criticism was defensiveness.
+I don’t handle criticism well. (You might find that hard to believe,
+after reading the rest of this post. Maybe it’s just the confrontation
+part I don’t handle well.) Thankfully the blow was softened somewhat,
+because the person providing the criticism didn’t tell me directly —
+they told Amos, a trusted colleague of mine. Amos then passed it on to
+me. I’m grateful for that — this is the best way for me to have received
+that criticism.

+

What I did next is the astonishing thing. I swallowed my pride for a
+minute. Not an easy thing for me to do, for all the usual reasons, and
+more. I decided to address the problem head on. If someone was telling
+someone else that I was causing some sort of a problem, then even that
+perception was a problem that I needed to address. So I decided to hold
+a retrospective for myself. Retrospectives are the way Agile teams address
+problems and improve how they work. If it would work for a team, I figured
+it should work for an individual.

+

I’d held retrospectives by myself before. Before I even knew about
+retrospectives, I’d taken some time to assess how I had done things
+on a couple of projects I had worked on as an independent contractor. But
+those were about technical decisions I had made and processes I had
+followed. This one would be different. This one was about a personal
+shortcoming, an action of mine that had caused some harm. This one
+involved feelings and emotions.

+

So I asked Amos to facilitate my one-person retro. We’d both done a lot
+of retrospectives, but neither one of us had done anything like this before.
+We had a bit of trouble getting started. We didn’t have any details
+about why the anonymous person felt the way he felt. So it was hard
+to address something that nebulous. So I asked Amos if he could follow
+up with the person. Luckily, the person was online and able to respond
+in real time.

+

So we talked things through some. We went on some tangents. We made some
+progress — things that I could write down as take-aways and action items.
+We got off-topic again, and then came back to it. Then Amos decided to
+play The Why Game. This worked amazingly well. It helped me to find the
+root reasons why I was holding back change. Basically I’m afraid of
+failure. Going deeper, we looked into why I’m afraid of failure. This
+line of inquiry was particularly effective. Hopefully it’ll be enough for
+me to stop being an impediment to change. Here are my notes / take-aways:

+
    +
  • Be careful playing devil’s advocate in public. +
      +
    • People will think I’m taking that stance.
    • +
    +
  • +
  • Don’t always take the boss’s side in public.
  • +
  • Don’t be the person to allow the team to take the easy path.
  • +
  • Why do I think that managers won’t let us try a 2nd time?
  • +
  • Why do I think we might fail the first time? +
      +
    • I want to put us in a better position to succeed.
    • +
    +
  • +
  • I’m being too conservative.
  • +
  • Be willing to fail. Don’t fail to try.
  • +
  • Don’t say we shouldn’t try because I think other people don’t want to try. +
      +
    • That is just self-perpetuating.
    • +
    +
  • +
  • I’m afraid of failing because I haven’t done it much. +
      +
    • Because I’m good at learning from other people’s failures.
    • +
    • But this is holding me back and making me cautious. +
        +
      • Despite the fact that I try to not be cautious.
      • +
      +
    • +
    • So I need to work to counter-act those fears.
    • +
    +
  • +
+

At first, I thought that I’d never heard of anyone doing anything like
+this. But then I recalled that I’d heard about somewhere that everyone
+does this, with their whole team. That would be hard. I’d have to
+trust everyone in the room.

+

But I would recommend this for anyone that can pull it off. It’s hard,
+but it has a large payoff. Just like a team retrospective, it feels good
+making this kind of breakthrough, and knowing that just spending that
+little amount of time has made me a better person. (Mine took about an hour.)
+I think the hardest part is finding someone (or a group of people) you
+trust to be your facilitator.

+

I’ve decided to call this thing an introspective. A retrospective is
+about looking back. This is about looking inward. I’d be interested to
+find out who is doing this kind of thing. Does it have a commonly accepted
+name? How does it work? What techniques work best? If you’ve got any answers or ideas, please comment below, or tweet me @CraigBuchek.

+

So thank you to that anonymous person. Your way of addressing this was
+probably more effective than you could have imagined that it might be.
+I hope that this exercise will help me make the changes required, to make
+me a better person, and help the team become the best that we can be.

+

Amos has written up his thoughts on the introspective.

+
+ + +
+ +
+ +

+ 2 thoughts on “Introspective”

+ + +
    +
  1. + +
  2. +
  3. + +
  4. +
+ + + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/01/26/testing-rails-validators.html b/public/2014/01/26/testing-rails-validators.html new file mode 100644 index 0000000..4103065 --- /dev/null +++ b/public/2014/01/26/testing-rails-validators.html @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + +Testing Rails Validators – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Testing Rails Validators

+ + + +
+

It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/01/26/testing-rails-validators/feed b/public/2014/01/26/testing-rails-validators/feed new file mode 100644 index 0000000..d9861d1 --- /dev/null +++ b/public/2014/01/26/testing-rails-validators/feed @@ -0,0 +1,18 @@ + + + Comments on: Testing Rails Validators + + http://blog.boochtek.com/2014/01/26/testing-rails-validators + Web Development, Ruby on Rails, Open Source + Thu, 03 Dec 2015 08:53:51 +0000 + hourly + 1 + https://wordpress.org/?v=4.4 + + diff --git a/public/2014/02.html b/public/2014/02.html new file mode 100644 index 0000000..70144b7 --- /dev/null +++ b/public/2014/02.html @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + +February 2014 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Chording Keyers

+ + + +
+

I’m considering buying a chording keyer. A keyer is a 1-handed text input device. Chording means that you can hit more than 1 key at a time. I’ve been interested in these for a long time actually. They’ve been popular in the wearable computing (“cyborg”) field, but never caught on anywhere else. I’ve always thought that 1-handed chording keyer would be a great input device to use with a cell phone. It’d be even better with a heads-up display like the Google Glass.

+

There are quite a few chording keyers available. It doesn’t appear that any will meet exactly what I’m really looking for, but I might take the leap anyway. It should be a hand-held device. I really want a Bluetooth device, so I can use it with a computer, phone, or tablet. Below are the ones I’m considering.

+

Twiddler

+

The Twiddler has been around quite a while, as probably the main commercially available keyer for wearable computing. There was a time that it was unavailable, but there’s a new version available from HandyKey now. It’s $200 ($220 Canadian), and comes in only USB.

+

The Twiddler looks like a small TV remote. The current version (2.1) has 4 thumb keys, a thumb joystick (for mouse), and 12 small finger keys. It has good community support, including an alternate chord layout.

+

In10did Decatxt

+

In10did has been trying to bring out a few variations of their devices. The Decatxt looks like a pack of cigarettes, with 8 keys on the front and 2 thumb keys. It can be used with 1 or 2 hands. All letters can be typed with 1 finger or 1 thumb and 1 finger.

+

The Decatxt has USB and Bluetooth models, but I’m unable to find pricing or a way to buy one. It looks like there have been no updates for over a year, so I’m concerned that this is going to be vaporware.

+

CyKey

+

The CyKey is based on the Microwriter, which goes back to the early 1980s. The Microwriter was a desktop-based device, but it looks like the CyKey might be able to be used as a hand-held device. It has 9 buttons on the face, arranged in 3 groups of 3 buttons.

+

The CyKey is $125 (£75) and works over InfraRed connected to USB. A Bluetooth model is supposedly in the works.

+

EkaPad

+

The EkaPad is like a cross between the Decatxt and the Twiddler. It has 12 buttons on the face, but no thumb keys. It’s got a nice well-thought-out design that looks quite ergonomic.

+

The EkaPad is $150, available in USB only. A desktop holder is available to hold it upright, or it can be used hand-held.

+

FrogPad

+

The FrogPad is meant to be used on a flat surface only. It looks kind of like a third of a laptop keyboard. It’s got 15 regular keys and 5 modifier keys. Only 1 key and 1 modifier can be chorded. Right-handed and left-handed models are available, with USB connectivity.

+

The FrogPad2 was recently announced, replacing the original FrogPad. It switches to standard desktop-style keys, and adds some more keys along the top. It supports USB On-the-Go and Bluetooth. I’m not really interested in this model with its regular keys, but it doesn’t appear that the original version is being made any longer. The new models are priced at $200. I believe the old models were $150.

+

Chordite

+

The Chordite looks the most promising, but it’s only a concept with some DIY prototypes. It’s a hand-held device, with 2 buttons on each of the 4 fingers — one activated by the distal phalanx and the other by the middle phalanx. This initially sounds like it would be difficult to press 2 different buttons with 1 finger, but I think the way they’re positioned would make it actually work really well.

+

Septambic Keyer

+

The septambic keyer is another DIY concept, hand-built and used by wearable computing folks. It’s fitted to your palm, and you squeeze your fingers toward a fist to activate the buttons. There’s one button for each of the 4 fingers, and 3 for the thumb.

+

Clove 2

+

The Clove 2 is a Bluetooth-enabled glove with 5 buttons — 1 for each finger. It’s another do-it-yourself project.

+

Software Options

+

I found an interesting project called ASETNIOP that allows chording on a standard Qwerty keyboard. (Assuming the keyboard supports multi-key roll-over well enough. Most keyboards should work.) It’s got a really nice online interactive tutorial. You basically chord with the 8 home-row keys, plus the spacebar.

+

For Android, a program called GKOS for Android uses multi-touch with 1 or 2 button presses. The buttons are different sizes, depending on their frequency. I like this concept, but I don’t think the implementation is quite ready for production.

+

I also found an app for iOS called Nintype that is a combination between Swype and a chording keyer.

+

Conclusion

+

I think I’m going to try the Twiddler. If I find the concept to be a good one, hopefully I’ll build myself a Chordite (or have someone build one for me).

+

I’m also going to play with implementing some of the ideas from ASETNIOP, using KeyRemap4MacBook.

+
+ + +
+ +
+
+ +

Includable ActiveRecord

+ + + +
+

I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+
+ + +
+ +
+
+ +

Empathy

+ + + +
+

I facilitated our team retrospective this morning. I felt like we made a little forward progress, but not as much as I would have liked. But it really brought one thing to the forefront of my thoughts today — empathy gained through communication.

+

We have a pretty large team by Agile standards — we had 20 people in our retro: 16 developers, 3 QA folks, and 1 manager. Out of those, only about 5 or 6 speak up regularly. I recently sent out a survey to the team, trying to get feedback on how we could improve our retros. A couple of the questions tried to get a feel for why people aren’t speaking up more. Only about half the people responded, and the answers didn’t really answer my question as well as I had hoped.

+

So on Amos‘s suggestion, we did the Safety Check exercise. We got a good set of answers to why people don’t feel safe. About half of the answers were about the fear of looking stupid in front of other people. About half of those mentioned the manager — they’re worried they might get in trouble for what they say. We talked some about fear and how it’s more often than not misplaced. And that the worst consequences are usually not as bad as you might think. But then we got to the crux of the problem — there’s not enough trust amongst the team, and especially between the team members and the manager.

+

About half of our team is new (within the past 6 months) — including the manager. While the developers have made some good progress building trust amongst each other, we haven’t had as much time with the manager to build trust between him and the rest of the team. So the lack of trust isn’t at all surprising.

+

Honestly, I already knew we had trust issues, and wanted to address them, but needed a way to lead the team to that same realization. With this exercise driving out the issue, we were then able to have a conversation about trust. The conversation was pretty good. We got more voices to contribute than probably any other topic we’d previously covered. (I was disappointed that the manager kept quiet though. I later found that he was trying to mitigate people’s fears by keeping quiet, but I urged him to contribute more in the future.)

+

But one point really stood out in my mind — a point of view that I hadn’t previously thought much about. Lauren, one of our QA analysts, pointed out that most human communication is non-verbal. We give tons of cues via body language, facial expressions, eye contact, tone of voice, even posture. I don’t recall if Lauren said it explicitly, but she pointed out that these cues build empathy between the speakers. She encouraged us to use more voice chat and video chat, as opposed to IRC text chat, because it would create more empathy between the people communicating, which would lead to more trust.

+

I spent most of the rest of the day talking to people on the phone or via Google Hangouts voice. And every single time, I consciously noticed that I was gaining empathy for the person I was speaking with. I assume (and hope) that that’s working both ways. I suppose that it’s always been happening, but I never really noticed it.

+

I’ve heard a lot of talk about empathy among Agile practitioners lately. It’s been mentioned on the Ruby Rogues podcast, and Angela Harms has been preaching it for years. I already understood how important it is. But until today, I didn’t really feel it happening.

+

So if you’re looking to build trust with someone, spend some time talking with them. Preferably in person, but if that’s not possible, seriously consider video or voice modes of communication, instead of sending an email or an instant message.

+

 

+
+ + +
+ +
+
+ +

What I Want in a Blog Engine

+ + + +
+

I’m considering moving away from WordPress, for a couple reasons:

+
    +
  • Security. There have been several vulnerabilities over the past few years, and I’ve never really had a high level of confidence that it’s secure. In addition, I now find the whole PHP model — every web app running as a single user, instead of leveraging UNIX permissions — to be broken.
  • +
  • Speed. I started to realize a couple years ago that a blog engine should generate static pages. The static pages should be updated whenever new content is added. There’s really no reason to re-generate the page every time someone wants to read it. At the very least, Server-Side Includes (SSI) or Edge-Side Includes (ESI) should be used.
  • +
+

Now that I’m starting to actually blog consistently, it makes sense to change. But before I move to something different, I want to be sure that I find all the features that I need. This post is my thought process about what I want.

+

Requirements

+
    +
  • Self hosted. I like to run my own servers. Partly so I can keep full control, and partly because I enjoy doing some system admin stuff (as long as it doesn’t take too much time).
  • +
  • Generation of static pages. Almost everything should be a static page by the time a user requests a page. Obviously, submitting comments would hit a dynamic page, and comment approval would likely be done through a dynamic part of the site. But publishing a blog post or comments should generate static files to be served by Apache or nginx.
  • +
  • Categories (or maybe tags). I want readers to be able to find other things that I’ve written that might interest them. A list of categories in a sidebar would help them find what interests them.
  • +
  • Site map. Especially an XML sitemap, so Google can index better. I also like the idea of readers being able to see everything in one place.
  • +
  • Archives. This is another way of letting readers see everything I’ve written, organized by publication date.
  • +
  • RSS and/or Atom. I want readers to be able to subscribe to my blog.
  • +
  • Media. I don’t think I necessarily need a media manager, just a simple way to add pictures to blog posts.
  • +
  • Comments. I want to allow comments, but I don’t want to use anything like Disqus. I want the comments to be in my own database, so they won’t disappear. I also dislike requiring JavaScript just to see comments. And I’ve worked at places where Disqus wouldn’t work, due to web proxy servers. Comments should be moderated by the site admin, editor, or author. Readers should not have to log in to post a comment, but it would be nice to allow users to log into Twitter or some other social network to provide their info.
  • +
  • Pingbacks and Tweetbacks. I’d like to be able to see who’s referring to my articles.
  • +
+

Nice to Have

+
    +
  • Free. I almost consider this to be a requirement, but I’d be willing to entertain a 1-time payment for something that meets my needs really well. I need to try to counteract my DIY-at-all-non-monetary-costs mentality.
  • +
  • Open Source. I think Open Source is likely to provide a more useful product, especially in this space. An Open Source option is more likely to improve over time.
  • +
  • Not PHP. I’ve got several problems with PHP. First, the whole PHP model of running in the same context as Apache (or nginx) breaks UNIX conventions. A security issue in a PHP program exposes more than just the program itself — it exposes Apache and every other PHP program. Under the UNIX model, each program runs in a different user context, isolating bugs to that program only. PHP is also a “hacky” language. PHP programmers tend to not be as rigorous (in many ways), leading to more vulnerabilities. Finally, the PHP language itself has had several misfeatures that have lead to security issues — much more than Ruby or Java.
  • +
  • Not MySQL. I’d like to be able to remove MySQL from my servers. PostgreSQL would be much preferred. Storing everything in git would be fine too — maybe even preferred. I’d even be fine with MongoDB or some other simple NoSQL database.
  • +
  • Admin console. I can’t imagine approving comments without this, but I’m not closed to that possibility.
  • +
  • Edit history. I want to be able to update posts, but see what the previous versions looked like. (Not sure if I care whether the public can or cannot see the various revisions.)
  • +
  • WYSIWYG. I’d be willing to settle for Markdown. In fact, I’d prefer (extended) Markdown as the canonical form behind the WYSIWYG.
  • +
  • Code highlighting. A lot of my blog posts (will) include code in various languages. I’d like the blogging software to be able to handle that well. Syntax highlighting would be great. Any other related features would also be nice.
  • +
  • Through-the-web editing. I’d like to be able to edit a page from any device from anywhere that has Internet access.
  • +
  • Reader-submitted edits. This is probably going to be the hardest thing to find, but I’d like a really easy way for readers to let me know of typos and such by simply submitting a fix for them. The author or editor of the post would have to approve the edits, obviously.
  • +
  • Import from WordPress. I don’t have a ton of content in WordPress, or else this would probably be a more firm requirement.
  • +
  • Community. Community helps software thrive, adding improvements and ensuring security.
  • +
  • Plugins. It would be nice for the blog engine to be extensible.
  • +
  • Unpublished drafts and previews. I’d really like to be able to see by blog posts before I release them to the world.
  • +
  • CMS abilities. I’d really like the ability to edit non-blog web pages the same way as blog entries.
  • +
  • Themes. It would be nice to have decently looking themes, so I could have other people help me make the site look nice.
  • +
  • Posting my tweets in a sidebar area. I’d be OK with this being done in client-side JavaScript. I’d also be OK with periodic polling of my Twitter feed, and updating the static content when appropriate.
  • +
+

Candidates

+

I’ve been playing with Jekyll lately, using it to generate a simple static site that can be updated easily. It’s pretty nice, and pretty easy to use. Something based on Jekyll would work well. The trickiest part will be figuring out how to manage comments. Or maybe I’ll just have to learn to live more minimally.

+

Octopress is the obvious candidate. It fits most of my needs and wants. Most likely I’ll write my own commenting engine and add it to my Octopress template.

+

The only other candidate that I’ve found is Ghost. It’s written in JavaScript and runs on Node. I don’t find Node to be that compelling, but the templates look great, and it’s designed for coding blogs.

+

I’ll probably take a quick look at Droplets, too. It’s written in PHP, but seems to fit my other requirements.

+

Unfortunately, that seems to be all there is in this space. Maybe I need to investigate more. Or maybe Octopress just has the static blog generation marked cornered. Either way, I’ll probably change my blogging software in the next month or two. Wish me luck!

+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2014/02/02/blogging-software.html b/public/2014/02/02/blogging-software.html new file mode 100644 index 0000000..eb05945 --- /dev/null +++ b/public/2014/02/02/blogging-software.html @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + +What I Want in a Blog Engine – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

What I Want in a Blog Engine

+ + + +
+

I’m considering moving away from WordPress, for a couple reasons:

+
    +
  • Security. There have been several vulnerabilities over the past few years, and I’ve never really had a high level of confidence that it’s secure. In addition, I now find the whole PHP model — every web app running as a single user, instead of leveraging UNIX permissions — to be broken.
  • +
  • Speed. I started to realize a couple years ago that a blog engine should generate static pages. The static pages should be updated whenever new content is added. There’s really no reason to re-generate the page every time someone wants to read it. At the very least, Server-Side Includes (SSI) or Edge-Side Includes (ESI) should be used.
  • +
+

Now that I’m starting to actually blog consistently, it makes sense to change. But before I move to something different, I want to be sure that I find all the features that I need. This post is my thought process about what I want.

+

Requirements

+
    +
  • Self hosted. I like to run my own servers. Partly so I can keep full control, and partly because I enjoy doing some system admin stuff (as long as it doesn’t take too much time).
  • +
  • Generation of static pages. Almost everything should be a static page by the time a user requests a page. Obviously, submitting comments would hit a dynamic page, and comment approval would likely be done through a dynamic part of the site. But publishing a blog post or comments should generate static files to be served by Apache or nginx.
  • +
  • Categories (or maybe tags). I want readers to be able to find other things that I’ve written that might interest them. A list of categories in a sidebar would help them find what interests them.
  • +
  • Site map. Especially an XML sitemap, so Google can index better. I also like the idea of readers being able to see everything in one place.
  • +
  • Archives. This is another way of letting readers see everything I’ve written, organized by publication date.
  • +
  • RSS and/or Atom. I want readers to be able to subscribe to my blog.
  • +
  • Media. I don’t think I necessarily need a media manager, just a simple way to add pictures to blog posts.
  • +
  • Comments. I want to allow comments, but I don’t want to use anything like Disqus. I want the comments to be in my own database, so they won’t disappear. I also dislike requiring JavaScript just to see comments. And I’ve worked at places where Disqus wouldn’t work, due to web proxy servers. Comments should be moderated by the site admin, editor, or author. Readers should not have to log in to post a comment, but it would be nice to allow users to log into Twitter or some other social network to provide their info.
  • +
  • Pingbacks and Tweetbacks. I’d like to be able to see who’s referring to my articles.
  • +
+

Nice to Have

+
    +
  • Free. I almost consider this to be a requirement, but I’d be willing to entertain a 1-time payment for something that meets my needs really well. I need to try to counteract my DIY-at-all-non-monetary-costs mentality.
  • +
  • Open Source. I think Open Source is likely to provide a more useful product, especially in this space. An Open Source option is more likely to improve over time.
  • +
  • Not PHP. I’ve got several problems with PHP. First, the whole PHP model of running in the same context as Apache (or nginx) breaks UNIX conventions. A security issue in a PHP program exposes more than just the program itself — it exposes Apache and every other PHP program. Under the UNIX model, each program runs in a different user context, isolating bugs to that program only. PHP is also a “hacky” language. PHP programmers tend to not be as rigorous (in many ways), leading to more vulnerabilities. Finally, the PHP language itself has had several misfeatures that have lead to security issues — much more than Ruby or Java.
  • +
  • Not MySQL. I’d like to be able to remove MySQL from my servers. PostgreSQL would be much preferred. Storing everything in git would be fine too — maybe even preferred. I’d even be fine with MongoDB or some other simple NoSQL database.
  • +
  • Admin console. I can’t imagine approving comments without this, but I’m not closed to that possibility.
  • +
  • Edit history. I want to be able to update posts, but see what the previous versions looked like. (Not sure if I care whether the public can or cannot see the various revisions.)
  • +
  • WYSIWYG. I’d be willing to settle for Markdown. In fact, I’d prefer (extended) Markdown as the canonical form behind the WYSIWYG.
  • +
  • Code highlighting. A lot of my blog posts (will) include code in various languages. I’d like the blogging software to be able to handle that well. Syntax highlighting would be great. Any other related features would also be nice.
  • +
  • Through-the-web editing. I’d like to be able to edit a page from any device from anywhere that has Internet access.
  • +
  • Reader-submitted edits. This is probably going to be the hardest thing to find, but I’d like a really easy way for readers to let me know of typos and such by simply submitting a fix for them. The author or editor of the post would have to approve the edits, obviously.
  • +
  • Import from WordPress. I don’t have a ton of content in WordPress, or else this would probably be a more firm requirement.
  • +
  • Community. Community helps software thrive, adding improvements and ensuring security.
  • +
  • Plugins. It would be nice for the blog engine to be extensible.
  • +
  • Unpublished drafts and previews. I’d really like to be able to see by blog posts before I release them to the world.
  • +
  • CMS abilities. I’d really like the ability to edit non-blog web pages the same way as blog entries.
  • +
  • Themes. It would be nice to have decently looking themes, so I could have other people help me make the site look nice.
  • +
  • Posting my tweets in a sidebar area. I’d be OK with this being done in client-side JavaScript. I’d also be OK with periodic polling of my Twitter feed, and updating the static content when appropriate.
  • +
+

Candidates

+

I’ve been playing with Jekyll lately, using it to generate a simple static site that can be updated easily. It’s pretty nice, and pretty easy to use. Something based on Jekyll would work well. The trickiest part will be figuring out how to manage comments. Or maybe I’ll just have to learn to live more minimally.

+

Octopress is the obvious candidate. It fits most of my needs and wants. Most likely I’ll write my own commenting engine and add it to my Octopress template.

+

The only other candidate that I’ve found is Ghost. It’s written in JavaScript and runs on Node. I don’t find Node to be that compelling, but the templates look great, and it’s designed for coding blogs.

+

I’ll probably take a quick look at Droplets, too. It’s written in PHP, but seems to fit my other requirements.

+

Unfortunately, that seems to be all there is in this space. Maybe I need to investigate more. Or maybe Octopress just has the static blog generation marked cornered. Either way, I’ll probably change my blogging software in the next month or two. Wish me luck!

+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/02/07/empathy.html b/public/2014/02/07/empathy.html new file mode 100644 index 0000000..ca493a6 --- /dev/null +++ b/public/2014/02/07/empathy.html @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + +Empathy – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Empathy

+ + + +
+

I facilitated our team retrospective this morning. I felt like we made a little forward progress, but not as much as I would have liked. But it really brought one thing to the forefront of my thoughts today — empathy gained through communication.

+

We have a pretty large team by Agile standards — we had 20 people in our retro: 16 developers, 3 QA folks, and 1 manager. Out of those, only about 5 or 6 speak up regularly. I recently sent out a survey to the team, trying to get feedback on how we could improve our retros. A couple of the questions tried to get a feel for why people aren’t speaking up more. Only about half the people responded, and the answers didn’t really answer my question as well as I had hoped.

+

So on Amos‘s suggestion, we did the Safety Check exercise. We got a good set of answers to why people don’t feel safe. About half of the answers were about the fear of looking stupid in front of other people. About half of those mentioned the manager — they’re worried they might get in trouble for what they say. We talked some about fear and how it’s more often than not misplaced. And that the worst consequences are usually not as bad as you might think. But then we got to the crux of the problem — there’s not enough trust amongst the team, and especially between the team members and the manager.

+

About half of our team is new (within the past 6 months) — including the manager. While the developers have made some good progress building trust amongst each other, we haven’t had as much time with the manager to build trust between him and the rest of the team. So the lack of trust isn’t at all surprising.

+

Honestly, I already knew we had trust issues, and wanted to address them, but needed a way to lead the team to that same realization. With this exercise driving out the issue, we were then able to have a conversation about trust. The conversation was pretty good. We got more voices to contribute than probably any other topic we’d previously covered. (I was disappointed that the manager kept quiet though. I later found that he was trying to mitigate people’s fears by keeping quiet, but I urged him to contribute more in the future.)

+

But one point really stood out in my mind — a point of view that I hadn’t previously thought much about. Lauren, one of our QA analysts, pointed out that most human communication is non-verbal. We give tons of cues via body language, facial expressions, eye contact, tone of voice, even posture. I don’t recall if Lauren said it explicitly, but she pointed out that these cues build empathy between the speakers. She encouraged us to use more voice chat and video chat, as opposed to IRC text chat, because it would create more empathy between the people communicating, which would lead to more trust.

+

I spent most of the rest of the day talking to people on the phone or via Google Hangouts voice. And every single time, I consciously noticed that I was gaining empathy for the person I was speaking with. I assume (and hope) that that’s working both ways. I suppose that it’s always been happening, but I never really noticed it.

+

I’ve heard a lot of talk about empathy among Agile practitioners lately. It’s been mentioned on the Ruby Rogues podcast, and Angela Harms has been preaching it for years. I already understood how important it is. But until today, I didn’t really feel it happening.

+

So if you’re looking to build trust with someone, spend some time talking with them. Preferably in person, but if that’s not possible, seriously consider video or voice modes of communication, instead of sending an email or an instant message.

+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/02/10/includable-activerecord.html b/public/2014/02/10/includable-activerecord.html new file mode 100644 index 0000000..c989f59 --- /dev/null +++ b/public/2014/02/10/includable-activerecord.html @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + +Includable ActiveRecord – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Includable ActiveRecord

+ + + +
+

I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/02/23/chording-keyers.html b/public/2014/02/23/chording-keyers.html new file mode 100644 index 0000000..2c36d78 --- /dev/null +++ b/public/2014/02/23/chording-keyers.html @@ -0,0 +1,416 @@ + + + + + + + + + + + + + + + + +Chording Keyers – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Chording Keyers

+ + + +
+

I’m considering buying a chording keyer. A keyer is a 1-handed text input device. Chording means that you can hit more than 1 key at a time. I’ve been interested in these for a long time actually. They’ve been popular in the wearable computing (“cyborg”) field, but never caught on anywhere else. I’ve always thought that 1-handed chording keyer would be a great input device to use with a cell phone. It’d be even better with a heads-up display like the Google Glass.

+

There are quite a few chording keyers available. It doesn’t appear that any will meet exactly what I’m really looking for, but I might take the leap anyway. It should be a hand-held device. I really want a Bluetooth device, so I can use it with a computer, phone, or tablet. Below are the ones I’m considering.

+

Twiddler

+

The Twiddler has been around quite a while, as probably the main commercially available keyer for wearable computing. There was a time that it was unavailable, but there’s a new version available from HandyKey now. It’s $200 ($220 Canadian), and comes in only USB.

+

The Twiddler looks like a small TV remote. The current version (2.1) has 4 thumb keys, a thumb joystick (for mouse), and 12 small finger keys. It has good community support, including an alternate chord layout.

+

In10did Decatxt

+

In10did has been trying to bring out a few variations of their devices. The Decatxt looks like a pack of cigarettes, with 8 keys on the front and 2 thumb keys. It can be used with 1 or 2 hands. All letters can be typed with 1 finger or 1 thumb and 1 finger.

+

The Decatxt has USB and Bluetooth models, but I’m unable to find pricing or a way to buy one. It looks like there have been no updates for over a year, so I’m concerned that this is going to be vaporware.

+

CyKey

+

The CyKey is based on the Microwriter, which goes back to the early 1980s. The Microwriter was a desktop-based device, but it looks like the CyKey might be able to be used as a hand-held device. It has 9 buttons on the face, arranged in 3 groups of 3 buttons.

+

The CyKey is $125 (£75) and works over InfraRed connected to USB. A Bluetooth model is supposedly in the works.

+

EkaPad

+

The EkaPad is like a cross between the Decatxt and the Twiddler. It has 12 buttons on the face, but no thumb keys. It’s got a nice well-thought-out design that looks quite ergonomic.

+

The EkaPad is $150, available in USB only. A desktop holder is available to hold it upright, or it can be used hand-held.

+

FrogPad

+

The FrogPad is meant to be used on a flat surface only. It looks kind of like a third of a laptop keyboard. It’s got 15 regular keys and 5 modifier keys. Only 1 key and 1 modifier can be chorded. Right-handed and left-handed models are available, with USB connectivity.

+

The FrogPad2 was recently announced, replacing the original FrogPad. It switches to standard desktop-style keys, and adds some more keys along the top. It supports USB On-the-Go and Bluetooth. I’m not really interested in this model with its regular keys, but it doesn’t appear that the original version is being made any longer. The new models are priced at $200. I believe the old models were $150.

+

Chordite

+

The Chordite looks the most promising, but it’s only a concept with some DIY prototypes. It’s a hand-held device, with 2 buttons on each of the 4 fingers — one activated by the distal phalanx and the other by the middle phalanx. This initially sounds like it would be difficult to press 2 different buttons with 1 finger, but I think the way they’re positioned would make it actually work really well.

+

Septambic Keyer

+

The septambic keyer is another DIY concept, hand-built and used by wearable computing folks. It’s fitted to your palm, and you squeeze your fingers toward a fist to activate the buttons. There’s one button for each of the 4 fingers, and 3 for the thumb.

+

Clove 2

+

The Clove 2 is a Bluetooth-enabled glove with 5 buttons — 1 for each finger. It’s another do-it-yourself project.

+

Software Options

+

I found an interesting project called ASETNIOP that allows chording on a standard Qwerty keyboard. (Assuming the keyboard supports multi-key roll-over well enough. Most keyboards should work.) It’s got a really nice online interactive tutorial. You basically chord with the 8 home-row keys, plus the spacebar.

+

For Android, a program called GKOS for Android uses multi-touch with 1 or 2 button presses. The buttons are different sizes, depending on their frequency. I like this concept, but I don’t think the implementation is quite ready for production.

+

I also found an app for iOS called Nintype that is a combination between Swype and a chording keyer.

+

Conclusion

+

I think I’m going to try the Twiddler. If I find the concept to be a good one, hopefully I’ll build myself a Chordite (or have someone build one for me).

+

I’m also going to play with implementing some of the ideas from ASETNIOP, using KeyRemap4MacBook.

+
+ + +
+ +
+ +

+ 4 thoughts on “Chording Keyers”

+ + +
    +
  1. + +
  2. +
  3. +
    + + +
    +

    The In10did DecaTxt is available on eBay and Amazon now. I ordered one recently and had it shipped over from the USA, which was a bit expensive when customs fees are added. It arrived yesterday morning and I plugged it in after arriving home from work. Sadly, it appears to do nothing whatsoever. Not sure if this model is DOA (but surely they test them?), or what. If I can get it to work, or get a replacement shipped, I’ll try to record my WPM with the device every day for a couple of weeks, to see where the improvement curve starts to taper off.

    +
    + +
    +
      +
    1. +
      + + +
      +

      Update: It works just fine! Actually the problem was that my laptop only supports Bluetooth 3.0, but at least 4.0 is needed. It also wasn’t discoverable by my phone because Android 4.3 or newer is needed. Tested it on a newer phone and on my work PC and it connected straight away. Looking forward to trying it on an Android tablet and measuring my speed over a few weeks.

      +
      + +
      +
        +
      1. +
        + + +
        +

        I’m glad you were able to get the DecaTxt connected and working. We are planning for a second version with micro USB rather than mini and a few software changes like being able to shut off the Bluetooth antenna. I’d love to get your feedback if you would email me and offer suggestions.
        +Thanks,
        +Wayne

        +
        + +
        +
      2. +
      +
    2. +
    +
  4. +
+ + + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/02/23/chording-keyers/feed b/public/2014/02/23/chording-keyers/feed new file mode 100644 index 0000000..4f43ebe --- /dev/null +++ b/public/2014/02/23/chording-keyers/feed @@ -0,0 +1,68 @@ + + + Comments on: Chording Keyers + + http://blog.boochtek.com/2014/02/23/chording-keyers + Web Development, Ruby on Rails, Open Source + Wed, 21 Jun 2017 01:27:18 +0000 + hourly + 1 + https://wordpress.org/?v=4.7.2 + + By: Wayne Rasanen + http://blog.boochtek.com/2014/02/23/chording-keyers/comment-page-1#comment-61468 + + Wed, 21 Jun 2017 01:27:18 +0000 + http://blog.boochtek.com/?p=124#comment-61468 + + I’m glad you were able to get the DecaTxt connected and working. We are planning for a second version with micro USB rather than mini and a few software changes like being able to shut off the Bluetooth antenna. I’d love to get your feedback if you would email me and offer suggestions.
+Thanks,
+Wayne

+]]>
+
+ + By: Oisín + http://blog.boochtek.com/2014/02/23/chording-keyers/comment-page-1#comment-60320 + + Tue, 28 Jun 2016 22:02:28 +0000 + http://blog.boochtek.com/?p=124#comment-60320 + + Update: It works just fine! Actually the problem was that my laptop only supports Bluetooth 3.0, but at least 4.0 is needed. It also wasn’t discoverable by my phone because Android 4.3 or newer is needed. Tested it on a newer phone and on my work PC and it connected straight away. Looking forward to trying it on an Android tablet and measuring my speed over a few weeks.

+]]>
+
+ + By: Oisín + http://blog.boochtek.com/2014/02/23/chording-keyers/comment-page-1#comment-60319 + + Mon, 27 Jun 2016 23:15:39 +0000 + http://blog.boochtek.com/?p=124#comment-60319 + + The In10did DecaTxt is available on eBay and Amazon now. I ordered one recently and had it shipped over from the USA, which was a bit expensive when customs fees are added. It arrived yesterday morning and I plugged it in after arriving home from work. Sadly, it appears to do nothing whatsoever. Not sure if this model is DOA (but surely they test them?), or what. If I can get it to work, or get a replacement shipped, I’ll try to record my WPM with the device every day for a couple of weeks, to see where the improvement curve starts to taper off.

+]]>
+
+ + By: Matthias + http://blog.boochtek.com/2014/02/23/chording-keyers/comment-page-1#comment-20675 + + Sat, 15 Nov 2014 14:48:17 +0000 + http://blog.boochtek.com/?p=124#comment-20675 + + Frogpad2 failed recently (see https://www.facebook.com/profile.php?id=105714169451329), but there is another possibility using freeware: http://www.autohotkey.com/ and scripts (easy adaptable!?):
+half querty: http://www.autohotkey.com/board/topic/1257-half-qwerty-one-handed-typing/
+frogpad: https://github.com/clarkm/dupepad/blob/master/frogpad.ahk
+Will be trying soon…

+]]>
+
+
+
diff --git a/public/2014/03.html b/public/2014/03.html new file mode 100644 index 0000000..a94cbb1 --- /dev/null +++ b/public/2014/03.html @@ -0,0 +1,359 @@ + + + + + + + + + + + + + + + +March 2014 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Brilliant – My Very Own Programming Language

+ + + +
+

I’ve decided to design and implement my own programming language, and call it Brilliant.

+

I’ve been interested in programming languages and linguistics almost as long as I’ve been using computers. I’ve long thought that if I ever go back to college, it’s likely that I’ll concentrate on programming languages as a specialty.

+

My recent discovery and involvement with the Crystal programming language has gotten me excited about new language ideas. It’s also helped me realize that implementing a language myself is feasible, and that there’s no better time to start than now.

+

Implementation

+

For now, the Brilliant implementation doesn’t let you do much more than “Hello World”. But you gotta start somewhere. So this article isn’t really “Introducing Brilliant” so much as the start of a series of articles on its design and implementation.

+

I wanted to use a PEG (parsing expression grammar) parser for several reasons. For one, they seem to have gained a lot of popularity in the past few years. Also, PEGs cannot be ambiguous, which can solve a few difficult problems, such as the “dangling else“. Perhaps my favorite feature of PEG grammars is that you don’t need a separate tokenizer (lexer). This provides a nice advantage in that we can use keywords like “class” as variables, as long as they’re not used in a place where the keyword would make sense.

+

So knowing that I wanted to use a PEG, I had to find a PEG parser. I kind of wanted to use ANTLR, which has been a leading parser for many years. But the PEG seems to be new to version 4, and I couldn’t find any Ruby bindings for version 4. Treetop seems to be the most popular parser for Ruby, but I found the EBNF format that Rattler uses to be more to my taste. I think the fact that it’s newer also gives it a few advantages, having had a chance to learn some lessons from Treetop.

+

I thought about using the Rubinius VM, but decided to go with LLVM, mainly since it has slightly better docs for Ruby, and because it’s what Crystal uses. Also, it’s pretty easy to get it to compile to a binary executable or run in a JIT. In the future, I might consider switching to the Rubinius VM, the Erlang VM, or the Perl 6 VM (Parrot). But for now, I like the idea of being able to compile to a binary and easily interface with C, just like Crystal.

+

Goals

+

My main goal is to have fun playing around with language ideas.

+

I’ve found a really great language in Ruby, so I’ll be using it as a starting point. But Ruby does have its faults. In some ways, I want to answer the question “what would Ruby look like if we designed it today?”.

+

But I also want to explore other ideas. What if objects defaulted to immutable? What if functions and methods were assumed to be pure by default? Might it be possible to infer the purity of a function or method? (If so, we could automatically memoize them.) Can we make creating an actor as easy as creating an object?

+

I’ll also be looking at ideas from other programming languages. Could we get some of the benefits of Haskell’s purity without having to do somersaults to do IO? Could we mix Python’s indentation style scoping with brace style or begin/end style? Could we integrate Icon’s ideas about success and failure (goal-directed execution)? What interesting features can we pull from Ada, Io, CoffeeScript, Crystal, Rubinius, Perl 6, etc.?
+

+

I’m not so much as interested in cutting-edge features, as in features that can be easily used by the average programmer. More importantly, I’m interested in how features interact with each other, so they fit well together to create a whole that’s greater than the sum of the parts.

+

Naming

+

I wanted to name my programming language “Fermat”. I’ve long been intrigued by Fermat’s Last Theorem, and Fermat’s Little Theorem is important in number theory. Unfortunately, there’s already a computer algebra system with that name.

+

So I decided to find a name in the same vein as “Ruby” and “Crystal”. I looked at the Wikipedia page for “gemstones” for some inspiration. A lot of the names of gemstones are already taken. I considered some obscure gemstones, but saw the word “brilliant” and thought it was decent. It’s not the name of another gemstone, but still evokes some similarity to Ruby and Crystal.

+

So that’s the name for now. Perhaps I’ll decide to change it at some point in the future, but I needed a name for the project, as well as a file extension for source code files. I chose “bril” for that. I suppose “br” would be a better choice. Perhaps I’ll change that before the next article in this series.

+

Future

+

I hope to work on Brilliant every once in a while. I expect it’ll take a couple years before it’s really very useful.

+

When I do add a major feature, I’ll be certain to blog about it. I’ve got tons of ideas strewn about in various files. It would be great to get them organized and published —and even better to get them implemented.

+
+ + +
+ +
+
+ +

Estimation Isn’t Agile

+ + + +
+

I don’t believe that estimation should be part of any Agile practice.

+

One of our managers recently mentioned that we hadn’t met the “contract” that we had “committed” to in our last iteration. This was complete nonsense, because A) we hadn’t made any such commitments, and B) we completed many more story points than the previous iterations (and without inflating story points).

+

estimates-as-deadlines

+

But her language made me come to several realizations. First and foremost, estimates are contracts. Sure, they’re not supposed to be treated as commitments, but they almost always are. And what does the Agile Manifesto say about this? It says that we should value customer collaboration over contract negotiation, and responding to change over following a plan. So it’s pretty clear that treating estimates as commitments is completely counter to the Agile values.

+

Why does this matter? What benefits do the Agile values bring us? I think the biggest benefit they bring is changing the way that we work, so that we can better deliver value to our customers. Without Agile, we’d just keep working the way we’ve always done things. And that didn’t seem to be working out so well. If we follow the Agile values and principles, at least we’ll have a fighting chance of improving our ability to deliver value.

+

Ask yourself — have you ever seen a software development project that was on time and on budget? Where the estimates were spot-on? Of course not. For one, we’re terrible at estimating. For another, our plans change — either from external factors, or from what we learn as we go.

+

Improved Estimation

+

To me, Agile is also about facing reality — and embracing it. It realizes that we’re terrible at estimating. It realizes that plans change. Most Agile methodologies have some tricks to counteract Hofstadter’s law. Generally, we use relative story points instead of hours, and then use an empirical factor to convert points to hours.

+

When this works, it is better than any other estimation I’ve ever seen. But it doesn’t work very often. People have trouble with relative estimation. How do you set the basis for what a point means without relating it to actual hours? Affinity estimation could work, but then you have to remember what the basis was. We’ve got a large distributed team, and when we tried this, we couldn’t all remember what the basis was.

+

Since we couldn’t get affinity estimation to work, we tried changing to perfect hours (only powers of 2). But then people thought of them as time. When we took longer than the estimate on an individual story, managers and team members thought we were taking longer than we should have. So our estimates ended up causing problems.

+

What Can We Do Instead?

+

Managers want estimates so that they can have predictability. They want to know when new features will be available. Is there a better way to get what we need?

+

I believe there’s a better way — prioritization. If you work on the most important thing first, then the most important thing will get done first. We should always be working on the next most important thing.

+

What if there’s more than 1 thing that’s most important? Then you’ve failed. You’ve failed at logic if you can’t understand that only 1 thing can be most important. You’ve failed at prioritizing the customers’ needs. You’ve failed at project management.

+

Arguments

+

1. Why can’t you just tell us how long it will really take?

+

Because we don’t know. Because we can’t know. This is the first time we’ve ever implemented the functionality you’ve asked for. If we’d done it before, we’d just use that existing code. As Glenn Vanderburg pointed out in his excellent talk on Software Engineering, we’re not building software, we’re architecting it.

+

2. But we have to tell our customers what to expect.

+

Why? Is the product so bad that you can’t keep customers around without leading them on with future enhancements? And why do customers need exact dates? A general roadmap telling them what the priorities for upcoming features should be sufficient.

+

3. But we have to have messaging about new features.

+

OK. Then send out that messaging once the feature has made it to Staging. Or even after it’s been rolled out to Production.

+

4. But we’ve promised these new features to the customers by this date.

+

Ah, so you’ve made promises to the customer that you don’t have control over. Have you ever heard of “under-promise and over-deliver”? That’s how you create happy customers. Yet you’ve done just the opposite, haven’t you? And then you want to blame someone else.

+

Risk

+

Estimates are risk. But the risk doesn’t come at the end, when the estimates are shown to be incorrect. The risk was in asking for the estimates in the first place, and placing trust in them. Don’t do it. Don’t promise things that you can’t be sure of.

+

Embrace this reality. Embrace this uncertainty. Always focus on what’s most important. That’s how you make customers happy.

+
+ + +
+ +
+
+ +

Slow Down!

+ + + +
+

There’s a tweet that I saw recently, with some simple advice for novice programmers:

+

Slow down.

+

This is probably good advice for most programmers. Our team recently noticed that every time we try to rush things, we make mistakes. And the mistakes end up costing us more time than if we had just done things at our normal pace. Slowing down ensures that you do things right, and when you do things right, you end up with a higher-quality product.

+

Speed and Code Quality

+

There are 2 types of code quality: internal and external. External code quality can be measured by how many bugs have been reported by customers. Internal code quality is harder to measure, but it mainly deals with the ability to change the code. When your internal quality is low, you’ve got lots of technical debt, and it’s harder to make changes.

+

So when you try to write code quickly, code quality decreases, leading to a code base that takes more time to make changes to. Conversely, when you slow down, your code quality improves, and it becomes easier to make changes more quickly. So when writing code, slowing down in the short run leads to a speed-up in the long run.

+

Speed and Process Improvement

+

But writing code isn’t the only place where we try to speed up. On an Agile team, we’re always trying to improve the way we work — especially at the beginning stages of an Agile transformation. So we’re eager to make changes in our processes. But I’d urge you to slow down here as well.

+

My colleague Amos and I frequently argue over pair switching. It’s funny, because we agree on everything except for 1 small detail. We both think pair switching is very important, to ensure that team members see more of what’s going on, to bring more ideas to each story, to prevent knowledge silos, and to encourage team ownership. Where we disagree is how long an ideal pairing session should last. I think pairs should switch every 2 hours, and he thinks 1 hour is ideal. I’ve seen teams reach the 1 hour pairing sessions successfully. But usually not without some pain and even often failing at the first attempt.

+

There’s nothing inherently wrong with failing. But if you fail at something, you’re not likely to try again. After all, you should learn from your failures, right?

+

So if you want your team to do something, you probably don’t want them to fail at it. If they fail, they won’t want to try a second time. That’s just human nature, and learning from failure. While you might think that they failed because they weren’t ready for the change yet, they’ll most likely think that they failed because this particular change won’t work for their situation. And they probably won’t know what to change when trying again, so they won’t try again.

+

I’ve seen this over and over. Back when Linux was up-and-coming, when a consultant pushed a company into using Linux before they were ready for it, and it didn’t work out, that company was cautious about trying again. So instead of being on the leading edge of using Linux, or even the middle of the pack, they ended up more toward the trailing edge. Had they not been pushed, they would have gotten more benefit in the long run.

+

So my advice in process improvement is the same as in programming: slow down. Take small steps toward what you think is the ideal. Make a small change, see how it works out, and adjust. As long as you’re still moving in the right direction, I believe you’ll move faster by taking small steps than by trying to make big leaps.

+
+ + +
+ +
+
+ +

Burying the Lede

+ + + +
+

Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+
+ + +
+ +
+
+ +

Yak Shaving #1: Cursor Keys

+ + + +
+

I recently decided to start using Emacs again. I used it extensively from the early 1990s until the early 2000s. I pretty much stopped using it when I had a sysadmin job with no Emacs on the servers, and no ability to install it. With the rising popularity of tmux and tmate for remote pairing, and my dislike for vim’s modes, I decided to try going back to Emacs in the terminal.

+

One thing I really want in a text editor is good cursor key support. Shifted cursor keys
+should select text, and Control+left and right should move by words. (Apple HIG says to use Option+left and right to move by words; most apps on Mac OS X seem to support both.) Things have worked this way on almost every text editor on every OS I’ve used — Amiga, DOS, Windows, NeXT, Mac, Motif, Gnome, KDE. It’s a part of the CUA standard that’s been in common usage on everything since the mid-1980s.

+

Enabling cursor keys in Emacs was pretty easy. I’ve decided to use Prelude to make getting started with Emacs easy. Emacs comes with the cursor keys enabled, but Prelude disables them. Undoing Prelude’s change is pretty easy:

+
(setq prelude-guru nil)
+

Trying to make shifted cursor keys work is where the trouble began. They work in the GUI version of Emacs, but not from the terminal. It turns out that the Mac Terminal doesn’t distinguish between cursor keys and shifted cursor keys in its default configuration. So I had to figure out how to configure key bindings in Terminal.

+

That’s easy enough — they’re in the preferences. But what should I set them to? This took a lot of research. Terminal emulation and ANSI code sequences are obscure, complex, and inconsistent. I eventually found the info I needed. For starters, Shift+Right, Shift+Left, Shift+Home, and Shift+End are defined in the terminfo. The rest I was able to piece together from various sources around the Internet.

+

I’m also trying to script my Mac configuration. So instead of manually adding all the keybindings in the Terminal preferences pane, I decided to write a script. Mac OS X does a decent job of allowing you to change preferences from the command line. For example, to always show the tab bar:

+
defaults write -app Terminal ShowTabBar 1
+

Easy enough, except for a couple problems. First, I had to figure out the obscure syntax used in the preferences for key codes. I was able to piece these together with some more Internet research. But the really big problem is that the keyboard bindings are 2 levels deep within a “dictionary” (hash map). And the defaults command doesn’t handle that. There are some obscure utilities that handle nested preferences, but they don’t work well with the preferences caching in Mac OS X 10.9 — a problem I ran into while testing.

+

So now I’m writing a utility in Python that does what the defaults command does, but that will handle nested dictionaries.

+

There’s a term for this process of having to solve one issue before you can solve another, and the issues get several layers deep. It’s called yak shaving.

+

Here’s another good example:

+
Father from Malcolm in the middle having to fix one thing before fixing another, ad infinitum.
Yak shaving – home repairs
+

I’m sure this will be the first of many posts about me yak shaving.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2014/03/03/yak-shaving-cursor-keys.html b/public/2014/03/03/yak-shaving-cursor-keys.html new file mode 100644 index 0000000..bf116a6 --- /dev/null +++ b/public/2014/03/03/yak-shaving-cursor-keys.html @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + +Yak Shaving #1: Cursor Keys – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Yak Shaving #1: Cursor Keys

+ + + +
+

I recently decided to start using Emacs again. I used it extensively from the early 1990s until the early 2000s. I pretty much stopped using it when I had a sysadmin job with no Emacs on the servers, and no ability to install it. With the rising popularity of tmux and tmate for remote pairing, and my dislike for vim’s modes, I decided to try going back to Emacs in the terminal.

+

One thing I really want in a text editor is good cursor key support. Shifted cursor keys
+should select text, and Control+left and right should move by words. (Apple HIG says to use Option+left and right to move by words; most apps on Mac OS X seem to support both.) Things have worked this way on almost every text editor on every OS I’ve used — Amiga, DOS, Windows, NeXT, Mac, Motif, Gnome, KDE. It’s a part of the CUA standard that’s been in common usage on everything since the mid-1980s.

+

Enabling cursor keys in Emacs was pretty easy. I’ve decided to use Prelude to make getting started with Emacs easy. Emacs comes with the cursor keys enabled, but Prelude disables them. Undoing Prelude’s change is pretty easy:

+
(setq prelude-guru nil)
+

Trying to make shifted cursor keys work is where the trouble began. They work in the GUI version of Emacs, but not from the terminal. It turns out that the Mac Terminal doesn’t distinguish between cursor keys and shifted cursor keys in its default configuration. So I had to figure out how to configure key bindings in Terminal.

+

That’s easy enough — they’re in the preferences. But what should I set them to? This took a lot of research. Terminal emulation and ANSI code sequences are obscure, complex, and inconsistent. I eventually found the info I needed. For starters, Shift+Right, Shift+Left, Shift+Home, and Shift+End are defined in the terminfo. The rest I was able to piece together from various sources around the Internet.

+

I’m also trying to script my Mac configuration. So instead of manually adding all the keybindings in the Terminal preferences pane, I decided to write a script. Mac OS X does a decent job of allowing you to change preferences from the command line. For example, to always show the tab bar:

+
defaults write -app Terminal ShowTabBar 1
+

Easy enough, except for a couple problems. First, I had to figure out the obscure syntax used in the preferences for key codes. I was able to piece these together with some more Internet research. But the really big problem is that the keyboard bindings are 2 levels deep within a “dictionary” (hash map). And the defaults command doesn’t handle that. There are some obscure utilities that handle nested preferences, but they don’t work well with the preferences caching in Mac OS X 10.9 — a problem I ran into while testing.

+

So now I’m writing a utility in Python that does what the defaults command does, but that will handle nested dictionaries.

+

There’s a term for this process of having to solve one issue before you can solve another, and the issues get several layers deep. It’s called yak shaving.

+

Here’s another good example:

+
Father from Malcolm in the middle having to fix one thing before fixing another, ad infinitum.
Yak shaving – home repairs
+

I’m sure this will be the first of many posts about me yak shaving.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/03/11/readable-shell-scripts.html b/public/2014/03/11/readable-shell-scripts.html new file mode 100644 index 0000000..131a900 --- /dev/null +++ b/public/2014/03/11/readable-shell-scripts.html @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + +Burying the Lede – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Burying the Lede

+ + + +
+

Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/03/16/slow-down.html b/public/2014/03/16/slow-down.html new file mode 100644 index 0000000..72fa992 --- /dev/null +++ b/public/2014/03/16/slow-down.html @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + +Slow Down! – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Slow Down!

+ + + +
+

There’s a tweet that I saw recently, with some simple advice for novice programmers:

+

Slow down.

+

This is probably good advice for most programmers. Our team recently noticed that every time we try to rush things, we make mistakes. And the mistakes end up costing us more time than if we had just done things at our normal pace. Slowing down ensures that you do things right, and when you do things right, you end up with a higher-quality product.

+

Speed and Code Quality

+

There are 2 types of code quality: internal and external. External code quality can be measured by how many bugs have been reported by customers. Internal code quality is harder to measure, but it mainly deals with the ability to change the code. When your internal quality is low, you’ve got lots of technical debt, and it’s harder to make changes.

+

So when you try to write code quickly, code quality decreases, leading to a code base that takes more time to make changes to. Conversely, when you slow down, your code quality improves, and it becomes easier to make changes more quickly. So when writing code, slowing down in the short run leads to a speed-up in the long run.

+

Speed and Process Improvement

+

But writing code isn’t the only place where we try to speed up. On an Agile team, we’re always trying to improve the way we work — especially at the beginning stages of an Agile transformation. So we’re eager to make changes in our processes. But I’d urge you to slow down here as well.

+

My colleague Amos and I frequently argue over pair switching. It’s funny, because we agree on everything except for 1 small detail. We both think pair switching is very important, to ensure that team members see more of what’s going on, to bring more ideas to each story, to prevent knowledge silos, and to encourage team ownership. Where we disagree is how long an ideal pairing session should last. I think pairs should switch every 2 hours, and he thinks 1 hour is ideal. I’ve seen teams reach the 1 hour pairing sessions successfully. But usually not without some pain and even often failing at the first attempt.

+

There’s nothing inherently wrong with failing. But if you fail at something, you’re not likely to try again. After all, you should learn from your failures, right?

+

So if you want your team to do something, you probably don’t want them to fail at it. If they fail, they won’t want to try a second time. That’s just human nature, and learning from failure. While you might think that they failed because they weren’t ready for the change yet, they’ll most likely think that they failed because this particular change won’t work for their situation. And they probably won’t know what to change when trying again, so they won’t try again.

+

I’ve seen this over and over. Back when Linux was up-and-coming, when a consultant pushed a company into using Linux before they were ready for it, and it didn’t work out, that company was cautious about trying again. So instead of being on the leading edge of using Linux, or even the middle of the pack, they ended up more toward the trailing edge. Had they not been pushed, they would have gotten more benefit in the long run.

+

So my advice in process improvement is the same as in programming: slow down. Take small steps toward what you think is the ideal. Make a small change, see how it works out, and adjust. As long as you’re still moving in the right direction, I believe you’ll move faster by taking small steps than by trying to make big leaps.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/03/23/agile-estimation.html b/public/2014/03/23/agile-estimation.html new file mode 100644 index 0000000..92adc4c --- /dev/null +++ b/public/2014/03/23/agile-estimation.html @@ -0,0 +1,372 @@ + + + + + + + + + + + + + + + + +Estimation Isn’t Agile – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Estimation Isn’t Agile

+ + + +
+

I don’t believe that estimation should be part of any Agile practice.

+

One of our managers recently mentioned that we hadn’t met the “contract” that we had “committed” to in our last iteration. This was complete nonsense, because A) we hadn’t made any such commitments, and B) we completed many more story points than the previous iterations (and without inflating story points).

+

estimates-as-deadlines

+

But her language made me come to several realizations. First and foremost, estimates are contracts. Sure, they’re not supposed to be treated as commitments, but they almost always are. And what does the Agile Manifesto say about this? It says that we should value customer collaboration over contract negotiation, and responding to change over following a plan. So it’s pretty clear that treating estimates as commitments is completely counter to the Agile values.

+

Why does this matter? What benefits do the Agile values bring us? I think the biggest benefit they bring is changing the way that we work, so that we can better deliver value to our customers. Without Agile, we’d just keep working the way we’ve always done things. And that didn’t seem to be working out so well. If we follow the Agile values and principles, at least we’ll have a fighting chance of improving our ability to deliver value.

+

Ask yourself — have you ever seen a software development project that was on time and on budget? Where the estimates were spot-on? Of course not. For one, we’re terrible at estimating. For another, our plans change — either from external factors, or from what we learn as we go.

+

Improved Estimation

+

To me, Agile is also about facing reality — and embracing it. It realizes that we’re terrible at estimating. It realizes that plans change. Most Agile methodologies have some tricks to counteract Hofstadter’s law. Generally, we use relative story points instead of hours, and then use an empirical factor to convert points to hours.

+

When this works, it is better than any other estimation I’ve ever seen. But it doesn’t work very often. People have trouble with relative estimation. How do you set the basis for what a point means without relating it to actual hours? Affinity estimation could work, but then you have to remember what the basis was. We’ve got a large distributed team, and when we tried this, we couldn’t all remember what the basis was.

+

Since we couldn’t get affinity estimation to work, we tried changing to perfect hours (only powers of 2). But then people thought of them as time. When we took longer than the estimate on an individual story, managers and team members thought we were taking longer than we should have. So our estimates ended up causing problems.

+

What Can We Do Instead?

+

Managers want estimates so that they can have predictability. They want to know when new features will be available. Is there a better way to get what we need?

+

I believe there’s a better way — prioritization. If you work on the most important thing first, then the most important thing will get done first. We should always be working on the next most important thing.

+

What if there’s more than 1 thing that’s most important? Then you’ve failed. You’ve failed at logic if you can’t understand that only 1 thing can be most important. You’ve failed at prioritizing the customers’ needs. You’ve failed at project management.

+

Arguments

+

1. Why can’t you just tell us how long it will really take?

+

Because we don’t know. Because we can’t know. This is the first time we’ve ever implemented the functionality you’ve asked for. If we’d done it before, we’d just use that existing code. As Glenn Vanderburg pointed out in his excellent talk on Software Engineering, we’re not building software, we’re architecting it.

+

2. But we have to tell our customers what to expect.

+

Why? Is the product so bad that you can’t keep customers around without leading them on with future enhancements? And why do customers need exact dates? A general roadmap telling them what the priorities for upcoming features should be sufficient.

+

3. But we have to have messaging about new features.

+

OK. Then send out that messaging once the feature has made it to Staging. Or even after it’s been rolled out to Production.

+

4. But we’ve promised these new features to the customers by this date.

+

Ah, so you’ve made promises to the customer that you don’t have control over. Have you ever heard of “under-promise and over-deliver”? That’s how you create happy customers. Yet you’ve done just the opposite, haven’t you? And then you want to blame someone else.

+

Risk

+

Estimates are risk. But the risk doesn’t come at the end, when the estimates are shown to be incorrect. The risk was in asking for the estimates in the first place, and placing trust in them. Don’t do it. Don’t promise things that you can’t be sure of.

+

Embrace this reality. Embrace this uncertainty. Always focus on what’s most important. That’s how you make customers happy.

+
+ + +
+ +
+ +

+ 3 thoughts on “Estimation Isn’t Agile”

+ + +
    +
  1. +
    + + +
    +

    As soon as estimation, sizing or velocity becomes a requirement outside of the team, the process is broken.

    +

    If a team uses these internally as a tool for planning how much work they will accept, I have no problem with that – but they should look for opportunities to reduce the time spent on the numbers aspect of planning.

    +

    Teams should question what the manager actually needs, rather than blindly exposing planning numbers.

    +
    + +
    +
  2. +
  3. + +
  4. +
  5. +
    + + +
    +

    2. But we have to tell our customers what to expect.
    +Why? Is the product so bad that you can’t keep customers around without leading them on with future enhancements? And why do customers need exact dates?

    +

    A lot of cases when you have to make brand new feature faster than your competitor or you have to meet deadline with other external things (like releasing iOS8, whatever). Or just an example when you are expecting a house will be done before winter comes so you can live in there, but workers didn’t finish it in time. Would you like to have no promises on deadline and make a decisions on each day of life? I don’t think so. Agile is for business, and if doesn’t make a things enhancing income – it doesn’t work.

    +

    What if there’s more than 1 thing that’s most important? Then you’ve failed.

    +

    If you have only one type of customers and one main functionality of the product – then you are right; But when product have several brunches, a lot of functionalities for several customers segment that should be delivered in one time – you can’t concentrate on one thing. Otherwise, your business fails.

    +

    I do understand why Agile can’t answer on question When? But it still doesn’t help with making a business in brutal, full of competitors world J.

    +
    + +
    +
  6. +
+ + + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/03/30/brilliant-my-own-programming-language.html b/public/2014/03/30/brilliant-my-own-programming-language.html new file mode 100644 index 0000000..2131ab6 --- /dev/null +++ b/public/2014/03/30/brilliant-my-own-programming-language.html @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + + +Brilliant – My Very Own Programming Language – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Brilliant – My Very Own Programming Language

+ + + +
+

I’ve decided to design and implement my own programming language, and call it Brilliant.

+

I’ve been interested in programming languages and linguistics almost as long as I’ve been using computers. I’ve long thought that if I ever go back to college, it’s likely that I’ll concentrate on programming languages as a specialty.

+

My recent discovery and involvement with the Crystal programming language has gotten me excited about new language ideas. It’s also helped me realize that implementing a language myself is feasible, and that there’s no better time to start than now.

+

Implementation

+

For now, the Brilliant implementation doesn’t let you do much more than “Hello World”. But you gotta start somewhere. So this article isn’t really “Introducing Brilliant” so much as the start of a series of articles on its design and implementation.

+

I wanted to use a PEG (parsing expression grammar) parser for several reasons. For one, they seem to have gained a lot of popularity in the past few years. Also, PEGs cannot be ambiguous, which can solve a few difficult problems, such as the “dangling else“. Perhaps my favorite feature of PEG grammars is that you don’t need a separate tokenizer (lexer). This provides a nice advantage in that we can use keywords like “class” as variables, as long as they’re not used in a place where the keyword would make sense.

+

So knowing that I wanted to use a PEG, I had to find a PEG parser. I kind of wanted to use ANTLR, which has been a leading parser for many years. But the PEG seems to be new to version 4, and I couldn’t find any Ruby bindings for version 4. Treetop seems to be the most popular parser for Ruby, but I found the EBNF format that Rattler uses to be more to my taste. I think the fact that it’s newer also gives it a few advantages, having had a chance to learn some lessons from Treetop.

+

I thought about using the Rubinius VM, but decided to go with LLVM, mainly since it has slightly better docs for Ruby, and because it’s what Crystal uses. Also, it’s pretty easy to get it to compile to a binary executable or run in a JIT. In the future, I might consider switching to the Rubinius VM, the Erlang VM, or the Perl 6 VM (Parrot). But for now, I like the idea of being able to compile to a binary and easily interface with C, just like Crystal.

+

Goals

+

My main goal is to have fun playing around with language ideas.

+

I’ve found a really great language in Ruby, so I’ll be using it as a starting point. But Ruby does have its faults. In some ways, I want to answer the question “what would Ruby look like if we designed it today?”.

+

But I also want to explore other ideas. What if objects defaulted to immutable? What if functions and methods were assumed to be pure by default? Might it be possible to infer the purity of a function or method? (If so, we could automatically memoize them.) Can we make creating an actor as easy as creating an object?

+

I’ll also be looking at ideas from other programming languages. Could we get some of the benefits of Haskell’s purity without having to do somersaults to do IO? Could we mix Python’s indentation style scoping with brace style or begin/end style? Could we integrate Icon’s ideas about success and failure (goal-directed execution)? What interesting features can we pull from Ada, Io, CoffeeScript, Crystal, Rubinius, Perl 6, etc.?
+

+

I’m not so much as interested in cutting-edge features, as in features that can be easily used by the average programmer. More importantly, I’m interested in how features interact with each other, so they fit well together to create a whole that’s greater than the sum of the parts.

+

Naming

+

I wanted to name my programming language “Fermat”. I’ve long been intrigued by Fermat’s Last Theorem, and Fermat’s Little Theorem is important in number theory. Unfortunately, there’s already a computer algebra system with that name.

+

So I decided to find a name in the same vein as “Ruby” and “Crystal”. I looked at the Wikipedia page for “gemstones” for some inspiration. A lot of the names of gemstones are already taken. I considered some obscure gemstones, but saw the word “brilliant” and thought it was decent. It’s not the name of another gemstone, but still evokes some similarity to Ruby and Crystal.

+

So that’s the name for now. Perhaps I’ll decide to change it at some point in the future, but I needed a name for the project, as well as a file extension for source code files. I chose “bril” for that. I suppose “br” would be a better choice. Perhaps I’ll change that before the next article in this series.

+

Future

+

I hope to work on Brilliant every once in a while. I expect it’ll take a couple years before it’s really very useful.

+

When I do add a major feature, I’ll be certain to blog about it. I’ve got tons of ideas strewn about in various files. It would be great to get them organized and published —and even better to get them implemented.

+
+ + +
+ +
+ +

+ One thought on “Brilliant – My Very Own Programming Language”

+ + +
    +
  1. +
    + + +
    +

    Cool! Creating a programming language is very fun and you learn a lot in the process. It can sometimes be frustrating and very challenging. The most important thing is to never give up. I’m really curious about the syntax and semantic you have in mind. Good luck!

    +
    + +
    +
  2. +
+ + + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/04.html b/public/2014/04.html new file mode 100644 index 0000000..c5066d1 --- /dev/null +++ b/public/2014/04.html @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + +April 2014 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Ruby Pattern: Parameterized Module Inclusion

+ + + +
+

I’ve run across a pattern in Ruby lately that I really like. It solves some problems that I’ve struggled with for several years. Let me start with the problems.

+

Let’s say you want to include an ORM in a model class, and want to tell it what database table to use. Typically, you’d do this:

+
class User
+  include MyORM::Model
+  table 'people'
+end
+

But that table call is more like an option to the module inclusion than anything else. So what we’d really like is something like this:

+
class User
+  include MyORM::Model, table: 'people'
+end
+

But that’s not valid Ruby; include doesn’t let you pass anything other than a module.

+

So when I was learning about Virtus, I noticed that its example of how to include it is a bit different than the standard Ruby idiomatic include:

+
class User
+  include Virtus.model
+end
+

At first glance, it reads like the first example. But on closer inspection and consideration, it’s quite a bit different. Where MyORM::Model is a constant that refers to a module, Virtus.model is a method call. So there’s a method named model in the Virtus module. That method returns another module — which is exactly what’s needed in order to include it into our model class.

+

The easiest way to implement Virtus.model would be this:

+
module Virtus
+  def model
+    ::Virtus::Model
+  end
+end
+
+module Virtus::Model
+  # ...
+end
+

If Virtus.model doesn’t need to take any arguments, that’s perfectly fine. In fact, I’ve started to use this implementation of the pattern for modules that don’t need parameters.

+

Because Virtus.model is a method, we can also call it with options:

+
class User
+  include Virtus.model(constructor: false, mass_assignment: false)
+end
+

We could even pass a block. But how do we process those options? There are a few different ways. However we do it, we have to be sure to return a module. And we can create modules in a few different ways.

+

Virtus uses the builder pattern. It takes the parameters passed in and builds a module dynamically. By that, I mean that it calls Module.new and then adds methods to that module. It does this by mixing in other modules, but it could do it by dynamically defining methods as well.

+

I’ve never seen this pattern in any other language. It’s obviously only possible because we can dynamically create modules.

+

The use of this idiom seems to be catching on a bit in the Ruby community. I’ve started using it myself, and will be adding it to my Includable::ActiveRecord gem soon.

+

 

+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2014/04/14/ruby-parameterized-module-inclusion.html b/public/2014/04/14/ruby-parameterized-module-inclusion.html new file mode 100644 index 0000000..bedc3a9 --- /dev/null +++ b/public/2014/04/14/ruby-parameterized-module-inclusion.html @@ -0,0 +1,394 @@ + + + + + + + + + + + + + + + + +Ruby Pattern: Parameterized Module Inclusion – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Ruby Pattern: Parameterized Module Inclusion

+ + + +
+

I’ve run across a pattern in Ruby lately that I really like. It solves some problems that I’ve struggled with for several years. Let me start with the problems.

+

Let’s say you want to include an ORM in a model class, and want to tell it what database table to use. Typically, you’d do this:

+
class User
+  include MyORM::Model
+  table 'people'
+end
+

But that table call is more like an option to the module inclusion than anything else. So what we’d really like is something like this:

+
class User
+  include MyORM::Model, table: 'people'
+end
+

But that’s not valid Ruby; include doesn’t let you pass anything other than a module.

+

So when I was learning about Virtus, I noticed that its example of how to include it is a bit different than the standard Ruby idiomatic include:

+
class User
+  include Virtus.model
+end
+

At first glance, it reads like the first example. But on closer inspection and consideration, it’s quite a bit different. Where MyORM::Model is a constant that refers to a module, Virtus.model is a method call. So there’s a method named model in the Virtus module. That method returns another module — which is exactly what’s needed in order to include it into our model class.

+

The easiest way to implement Virtus.model would be this:

+
module Virtus
+  def model
+    ::Virtus::Model
+  end
+end
+
+module Virtus::Model
+  # ...
+end
+

If Virtus.model doesn’t need to take any arguments, that’s perfectly fine. In fact, I’ve started to use this implementation of the pattern for modules that don’t need parameters.

+

Because Virtus.model is a method, we can also call it with options:

+
class User
+  include Virtus.model(constructor: false, mass_assignment: false)
+end
+

We could even pass a block. But how do we process those options? There are a few different ways. However we do it, we have to be sure to return a module. And we can create modules in a few different ways.

+

Virtus uses the builder pattern. It takes the parameters passed in and builds a module dynamically. By that, I mean that it calls Module.new and then adds methods to that module. It does this by mixing in other modules, but it could do it by dynamically defining methods as well.

+

I’ve never seen this pattern in any other language. It’s obviously only possible because we can dynamically create modules.

+

The use of this idiom seems to be catching on a bit in the Ruby community. I’ve started using it myself, and will be adding it to my Includable::ActiveRecord gem soon.

+

 

+

 

+
+ + +
+ +
+ +

+ 2 thoughts on “Ruby Pattern: Parameterized Module Inclusion”

+ + +
    +
  1. +
    + + +
    +

    I think it would be interesting to try using a block as the config to your include. Make a capitalized method that returns a module. Then you could pass a block with curly braces to that method.

    +

    +
    class User
    +  include MyORM::Model {
    +    table "people"
    +  }
    +end
    +

    +
    + +
    +
  2. +
  3. +
    + + +
    +

    I seem to have forgotten to mention that this is related to the Factory pattern. In fact, I think it’s a special case of the Factory pattern.

    +

    I thought I was using this pattern on the following code, but realized that it’s just the Factory pattern:

    +

    +
    +module Preserves
    +  def self.repository(*options, &block)
    +    repository = Repository.new(*options)
    +    repository.instance_eval(&block)
    +    repository
    +  end
    +end
    +
    +UserRepository = Preserves.repository(model: User) do
    +end
    +
    +

    +
    + +
    +
  4. +
+ + + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2014/04/14/ruby-parameterized-module-inclusion/comment-page-1.html b/public/2014/04/14/ruby-parameterized-module-inclusion/comment-page-1.html new file mode 100644 index 0000000..d8fc46e --- /dev/null +++ b/public/2014/04/14/ruby-parameterized-module-inclusion/comment-page-1.html @@ -0,0 +1,266 @@ + + + + + + + + + + + + + + + + + + +Ruby Pattern: Parameterized Module Inclusion | BoochTek, LLC + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+
+ + + + + +
+
+

Ruby Pattern: Parameterized Module Inclusion

+ + +
+ +
+

I’ve run across a pattern in Ruby lately that I really like. It solves some problems that I’ve struggled with for several years. Let me start with the problems.

+

Let’s say you want to include an ORM in a model class, and want to tell it what database table to use. Typically, you’d do this:

+
class User
+  include MyORM::Model
+  table 'people'
+end
+

But that table call is more like an option to the module inclusion than anything else. So what we’d really like is something like this:

+
class User
+  include MyORM::Model, table: 'people'
+end
+

But that’s not valid Ruby; include doesn’t let you pass anything other than a module.

+

So when I was learning about Virtus, I noticed that its example of how to include it is a bit different than the standard Ruby idiomatic include:

+
class User
+  include Virtus.model
+end
+

At first glance, it reads like the first example. But on closer inspection and consideration, it’s quite a bit different. Where MyORM::Model is a constant that refers to a module, Virtus.model is a method call. So there’s a method named model in the Virtus module. That method returns another module — which is exactly what’s needed in order to include it into our model class.

+

The easiest way to implement Virtus.model would be this:

+
module Virtus
+  def model
+    ::Virtus::Model
+  end
+end
+
+module Virtus::Model
+  # ...
+end
+

If Virtus.model doesn’t need to take any arguments, that’s perfectly fine. In fact, I’ve started to use this implementation of the pattern for modules that don’t need parameters.

+

Because Virtus.model is a method, we can also call it with options:

+
class User
+  include Virtus.model(constructor: false, mass_assignment: false)
+end
+

We could even pass a block. But how do we process those options? There are a few different ways. However we do it, we have to be sure to return a module. And we can create modules in a few different ways.

+

Virtus uses the builder pattern. It takes the parameters passed in and builds a module dynamically. By that, I mean that it calls Module.new and then adds methods to that module. It does this by mixing in other modules, but it could do it by dynamically defining methods as well.

+

I’ve never seen this pattern in any other language. It’s obviously only possible because we can dynamically create modules.

+

The use of this idiom seems to be catching on a bit in the Ruby community. I’ve started using it myself, and will be adding it to my Includable::ActiveRecord gem soon.

+

 

+

 

+
+ + +
+ +
+ + +

+ 2 thoughts on “Ruby Pattern: Parameterized Module Inclusion

+ + +
    +
  1. +
    + + +

    I think it would be interesting to try using a block as the config to your include. Make a capitalized method that returns a module. Then you could pass a block with curly braces to that method.

    +

    +

    class User
    +  include MyORM::Model {
    +    table "people"
    +  }
    +end
    +

    +
    + + +
    + +
  2. +
  3. +
    + + +

    I seem to have forgotten to mention that this is related to the Factory pattern. In fact, I think it’s a special case of the Factory pattern.

    +

    I thought I was using this pattern on the following code, but realized that it’s just the Factory pattern:

    +

    +

    +module Preserves
    +  def self.repository(*options, &block)
    +    repository = Repository.new(*options)
    +    repository.instance_eval(&block)
    +    repository
    +  end
    +end
    +
    +UserRepository = Preserves.repository(model: User) do
    +end
    +
    +

    +
    + + +
    + +
  4. +
+ + + + +
+

Leave a Reply

+
+

Your email address will not be published. Required fields are marked *

+ +

+

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

+ + + +

+

+
+ +
+ + +
+
+ + +
+ + +
+ + + + + + \ No newline at end of file diff --git a/public/2014/04/14/ruby-parameterized-module-inclusion/feed b/public/2014/04/14/ruby-parameterized-module-inclusion/feed new file mode 100644 index 0000000..4c4f87c --- /dev/null +++ b/public/2014/04/14/ruby-parameterized-module-inclusion/feed @@ -0,0 +1,81 @@ + + + Comments on: Ruby Pattern: Parameterized Module Inclusion + + http://blog.boochtek.com/2014/04/14/ruby-parameterized-module-inclusion + Web Development, Ruby on Rails, Open Source + Wed, 13 May 2015 03:10:44 +0000 + hourly + 1 + http://wordpress.org/?v=3.8.1 + + By: Craig Buchek + http://blog.boochtek.com/2014/04/14/ruby-parameterized-module-inclusion/comment-page-1#comment-2730 + + Sat, 31 May 2014 15:49:14 +0000 + http://blog.boochtek.com/?p=184#comment-2730 + + I seem to have forgotten to mention that this is related to the Factory pattern. In fact, I think it’s a special case of the Factory pattern.

+

I thought I was using this pattern on the following code, but realized that it’s just the Factory pattern:

+

+

+module Preserves
+  def self.repository(*options, &block)
+    repository = Repository.new(*options)
+    repository.instance_eval(&block)
+    repository
+  end
+end
+
+UserRepository = Preserves.repository(model: User) do
+end
+
+

+]]>
+
+ + By: Amos King + http://blog.boochtek.com/2014/04/14/ruby-parameterized-module-inclusion/comment-page-1#comment-2718 + + Wed, 28 May 2014 03:48:51 +0000 + http://blog.boochtek.com/?p=184#comment-2718 + + I think it would be interesting to try using a block as the config to your include. Make a capitalized method that returns a module. Then you could pass a block with curly braces to that method.

+

+

class User
+  include MyORM::Model {
+    table "people"
+  }
+end
+

+]]>
+
+
+
diff --git a/public/2014/04/14/ruby-parameterized-module-inclusion?replytocom=2718.html b/public/2014/04/14/ruby-parameterized-module-inclusion?replytocom=2718.html new file mode 100644 index 0000000..0a87bb1 --- /dev/null +++ b/public/2014/04/14/ruby-parameterized-module-inclusion?replytocom=2718.html @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + +Ruby Pattern: Parameterized Module Inclusion | BoochTek, LLC + + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+
+ + + + + +
+
+

Ruby Pattern: Parameterized Module Inclusion

+ + +
+ +
+

I’ve run across a pattern in Ruby lately that I really like. It solves some problems that I’ve struggled with for several years. Let me start with the problems.

+

Let’s say you want to include an ORM in a model class, and want to tell it what database table to use. Typically, you’d do this:

+
class User
+  include MyORM::Model
+  table 'people'
+end
+

But that table call is more like an option to the module inclusion than anything else. So what we’d really like is something like this:

+
class User
+  include MyORM::Model, table: 'people'
+end
+

But that’s not valid Ruby; include doesn’t let you pass anything other than a module.

+

So when I was learning about Virtus, I noticed that its example of how to include it is a bit different than the standard Ruby idiomatic include:

+
class User
+  include Virtus.model
+end
+

At first glance, it reads like the first example. But on closer inspection and consideration, it’s quite a bit different. Where MyORM::Model is a constant that refers to a module, Virtus.model is a method call. So there’s a method named model in the Virtus module. That method returns another module — which is exactly what’s needed in order to include it into our model class.

+

The easiest way to implement Virtus.model would be this:

+
module Virtus
+  def model
+    ::Virtus::Model
+  end
+end
+
+module Virtus::Model
+  # ...
+end
+

If Virtus.model doesn’t need to take any arguments, that’s perfectly fine. In fact, I’ve started to use this implementation of the pattern for modules that don’t need parameters.

+

Because Virtus.model is a method, we can also call it with options:

+
class User
+  include Virtus.model(constructor: false, mass_assignment: false)
+end
+

We could even pass a block. But how do we process those options? There are a few different ways. However we do it, we have to be sure to return a module. And we can create modules in a few different ways.

+

Virtus uses the builder pattern. It takes the parameters passed in and builds a module dynamically. By that, I mean that it calls Module.new and then adds methods to that module. It does this by mixing in other modules, but it could do it by dynamically defining methods as well.

+

I’ve never seen this pattern in any other language. It’s obviously only possible because we can dynamically create modules.

+

The use of this idiom seems to be catching on a bit in the Ruby community. I’ve started using it myself, and will be adding it to my Includable::ActiveRecord gem soon.

+

 

+

 

+
+ + +
+ +
+ + +

+ 2 thoughts on “Ruby Pattern: Parameterized Module Inclusion

+ + +
    +
  1. +
    + + +

    I think it would be interesting to try using a block as the config to your include. Make a capitalized method that returns a module. Then you could pass a block with curly braces to that method.

    +

    +

    class User
    +  include MyORM::Model {
    +    table "people"
    +  }
    +end
    +

    +
    + + +
    + +
  2. +
  3. +
    + + +

    I seem to have forgotten to mention that this is related to the Factory pattern. In fact, I think it’s a special case of the Factory pattern.

    +

    I thought I was using this pattern on the following code, but realized that it’s just the Factory pattern:

    +

    +

    +module Preserves
    +  def self.repository(*options, &block)
    +    repository = Repository.new(*options)
    +    repository.instance_eval(&block)
    +    repository
    +  end
    +end
    +
    +UserRepository = Preserves.repository(model: User) do
    +end
    +
    +

    +
    + + +
    + +
  4. +
+ + + + +
+

Leave a Reply to Amos King Cancel reply

+
+

Your email address will not be published. Required fields are marked *

+ +

+

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

+ + + +

+

+
+ +
+ + +
+
+ + +
+ + +
+ + + + + + \ No newline at end of file diff --git a/public/2014/04/14/ruby-parameterized-module-inclusion?replytocom=2730.html b/public/2014/04/14/ruby-parameterized-module-inclusion?replytocom=2730.html new file mode 100644 index 0000000..15c8ae6 --- /dev/null +++ b/public/2014/04/14/ruby-parameterized-module-inclusion?replytocom=2730.html @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + +Ruby Pattern: Parameterized Module Inclusion | BoochTek, LLC + + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+
+ + + + + +
+
+

Ruby Pattern: Parameterized Module Inclusion

+ + +
+ +
+

I’ve run across a pattern in Ruby lately that I really like. It solves some problems that I’ve struggled with for several years. Let me start with the problems.

+

Let’s say you want to include an ORM in a model class, and want to tell it what database table to use. Typically, you’d do this:

+
class User
+  include MyORM::Model
+  table 'people'
+end
+

But that table call is more like an option to the module inclusion than anything else. So what we’d really like is something like this:

+
class User
+  include MyORM::Model, table: 'people'
+end
+

But that’s not valid Ruby; include doesn’t let you pass anything other than a module.

+

So when I was learning about Virtus, I noticed that its example of how to include it is a bit different than the standard Ruby idiomatic include:

+
class User
+  include Virtus.model
+end
+

At first glance, it reads like the first example. But on closer inspection and consideration, it’s quite a bit different. Where MyORM::Model is a constant that refers to a module, Virtus.model is a method call. So there’s a method named model in the Virtus module. That method returns another module — which is exactly what’s needed in order to include it into our model class.

+

The easiest way to implement Virtus.model would be this:

+
module Virtus
+  def model
+    ::Virtus::Model
+  end
+end
+
+module Virtus::Model
+  # ...
+end
+

If Virtus.model doesn’t need to take any arguments, that’s perfectly fine. In fact, I’ve started to use this implementation of the pattern for modules that don’t need parameters.

+

Because Virtus.model is a method, we can also call it with options:

+
class User
+  include Virtus.model(constructor: false, mass_assignment: false)
+end
+

We could even pass a block. But how do we process those options? There are a few different ways. However we do it, we have to be sure to return a module. And we can create modules in a few different ways.

+

Virtus uses the builder pattern. It takes the parameters passed in and builds a module dynamically. By that, I mean that it calls Module.new and then adds methods to that module. It does this by mixing in other modules, but it could do it by dynamically defining methods as well.

+

I’ve never seen this pattern in any other language. It’s obviously only possible because we can dynamically create modules.

+

The use of this idiom seems to be catching on a bit in the Ruby community. I’ve started using it myself, and will be adding it to my Includable::ActiveRecord gem soon.

+

 

+

 

+
+ + +
+ +
+ + +

+ 2 thoughts on “Ruby Pattern: Parameterized Module Inclusion

+ + +
    +
  1. +
    + + +

    I think it would be interesting to try using a block as the config to your include. Make a capitalized method that returns a module. Then you could pass a block with curly braces to that method.

    +

    +

    class User
    +  include MyORM::Model {
    +    table "people"
    +  }
    +end
    +

    +
    + + +
    + +
  2. +
  3. +
    + + +

    I seem to have forgotten to mention that this is related to the Factory pattern. In fact, I think it’s a special case of the Factory pattern.

    +

    I thought I was using this pattern on the following code, but realized that it’s just the Factory pattern:

    +

    +

    +module Preserves
    +  def self.repository(*options, &block)
    +    repository = Repository.new(*options)
    +    repository.instance_eval(&block)
    +    repository
    +  end
    +end
    +
    +UserRepository = Preserves.repository(model: User) do
    +end
    +
    +

    +
    + + +
    + +
  4. +
+ + + + +
+

Leave a Reply to Craig Buchek Cancel reply

+
+

Your email address will not be published. Required fields are marked *

+ +

+

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

+ + + +

+

+
+ +
+ + +
+
+ + +
+ + +
+ + + + + + \ No newline at end of file diff --git a/public/2014/05.html b/public/2014/05.html new file mode 100644 index 0000000..3d51634 --- /dev/null +++ b/public/2014/05.html @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + +May 2014 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

TDD Is Alive And Well

+ + + +
+

I went to RailsConf this year, and the very first talk was a keynote by David Heinemeier Hansson (DHH), the creator of Ruby on Rails. The TL;DR of his talk was “TDD rarely has value”. He followed up with a blog post the next day, titled “TDD is dead. Long live testing.“, and 2 more posts. I think this line of thought is terribly misguided, and causing more harm than good. This article is my response.

+

First, I would like to address the good points of the talk. He said that programming is pseudoscience, and that people want to tell us that there’s a secret to being a better programmer. But what it really takes is working hard — reading a lot of code, writing a lot of code, and rewriting a lot of code. He’s right. And I also agree with him that you should forget about patterns for a while when learning to code. Beginners try to throw patterns at a problem instead of letting the patterns emerge where they’re supposed to.

+

I don’t completely agree that programming is a pseudoscience. In some ways it is, but I think it’s more of a craft. It’s a craft, because there’s a lot of science involved, but there’s also an art to doing it well. And like any craft, you’re always working to get better. So to respond to DHH’s stance that “software is more like poetry than physics”, I think it falls
+somewhere in between.

+

With regard to the software engineering practices we use, there really isn’t much science available, mostly because it’s a soft science. That is, it’s really hard to isolate a single variable when comparing code between projects. And nobody has the time or money to write the same code so many times that the differences would be statistically significant.

+

So we don’t have much science on TDD. But we do have some. Here’s a collection of several: StudiesOfTestDrivenDevelopment. And here’s one that explicitly looks are the difference between test-first and test-last: Does Test-Driven Development Really Improve Software Design Quality? What do these tell us? They tell us that TDD costs us about 10-30% in short-term productivity; reduces bugs by 30-90%, and decreases code complexity by about 30%. As Code Complete tells us (in section 20.5, with studies to back it up), improving quality reduces development costs. So, like most Agile practices, this is a case where spending a bit more time in the short term leads to time savings in the long term.

+

The more important lesson in the talk was that you have to do what works best for you and your situation. If TDD doesn’t give better results, then either find out how to make it give better results, or stop using it. As we often say in the Agile world, Agile doesn’t mean that you can stop using your brain. While I think TDD is appropriate in most situations, there are cases where it’s not worth the additional up-front cost. If the most important thing for your project is time-to-market, then not testing might be the right decision for you.

+

To me, TDD provides a bunch of benefits. First and foremost, TDD is a design discipline. It ensures that I think about how my code will be used before I think about how to implement it. This is very powerful in ensuring that the code is well-written from the perspective of other code using it.

+

Tested code provides confidence to be able to make changes without breaking things. If we write tests after the code, we’re less likely to write them. Tests written after the code also tend to test the implementation instead of the desired functionality. What we really want is tests written as a specification. With tests as a specification, we can come back later and understand why code was written. Without tests, or with poor tests, we can’t understand why the code is there; if we want to rewrite it, we don’t have the confidence that we’re not missing something. Writing tests first also ensures that we only write the code that is needed to implement the required functionality.

+

I’m not sure why DHH hasn’t “gotten” TDD. I’m not sure if it’s because he’s  a better coder than average, or if he just thinks in a different way than most of us. I think it’s partly because he doesn’t understand TDD, which he admitted might be the case. And I think he’s conflating TDD and unit testing.

+

DHH is influential in the developer community, especially those newer to Ruby and Rails. People listen to what he has to say. I was happy to see almost every other speaker made fun of DHH’s ideas, and most of the crowd knew better. But there will be a lot of others who will hear DHH, respect his opinions, and not give TDD the try that it deserves. And that’s sad, because it will lead to an overall reduction in code quality in the world.

+

Here are some other people’s thoughts on the matter:

+ +

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2014/05/05/tdd-is-alive-and-well.html b/public/2014/05/05/tdd-is-alive-and-well.html new file mode 100644 index 0000000..5d4862a --- /dev/null +++ b/public/2014/05/05/tdd-is-alive-and-well.html @@ -0,0 +1,321 @@ + + + + + + + + + + + + + + + + +TDD Is Alive And Well – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

TDD Is Alive And Well

+ + + +
+

I went to RailsConf this year, and the very first talk was a keynote by David Heinemeier Hansson (DHH), the creator of Ruby on Rails. The TL;DR of his talk was “TDD rarely has value”. He followed up with a blog post the next day, titled “TDD is dead. Long live testing.“, and 2 more posts. I think this line of thought is terribly misguided, and causing more harm than good. This article is my response.

+

First, I would like to address the good points of the talk. He said that programming is pseudoscience, and that people want to tell us that there’s a secret to being a better programmer. But what it really takes is working hard — reading a lot of code, writing a lot of code, and rewriting a lot of code. He’s right. And I also agree with him that you should forget about patterns for a while when learning to code. Beginners try to throw patterns at a problem instead of letting the patterns emerge where they’re supposed to.

+

I don’t completely agree that programming is a pseudoscience. In some ways it is, but I think it’s more of a craft. It’s a craft, because there’s a lot of science involved, but there’s also an art to doing it well. And like any craft, you’re always working to get better. So to respond to DHH’s stance that “software is more like poetry than physics”, I think it falls
+somewhere in between.

+

With regard to the software engineering practices we use, there really isn’t much science available, mostly because it’s a soft science. That is, it’s really hard to isolate a single variable when comparing code between projects. And nobody has the time or money to write the same code so many times that the differences would be statistically significant.

+

So we don’t have much science on TDD. But we do have some. Here’s a collection of several: StudiesOfTestDrivenDevelopment. And here’s one that explicitly looks are the difference between test-first and test-last: Does Test-Driven Development Really Improve Software Design Quality? What do these tell us? They tell us that TDD costs us about 10-30% in short-term productivity; reduces bugs by 30-90%, and decreases code complexity by about 30%. As Code Complete tells us (in section 20.5, with studies to back it up), improving quality reduces development costs. So, like most Agile practices, this is a case where spending a bit more time in the short term leads to time savings in the long term.

+

The more important lesson in the talk was that you have to do what works best for you and your situation. If TDD doesn’t give better results, then either find out how to make it give better results, or stop using it. As we often say in the Agile world, Agile doesn’t mean that you can stop using your brain. While I think TDD is appropriate in most situations, there are cases where it’s not worth the additional up-front cost. If the most important thing for your project is time-to-market, then not testing might be the right decision for you.

+

To me, TDD provides a bunch of benefits. First and foremost, TDD is a design discipline. It ensures that I think about how my code will be used before I think about how to implement it. This is very powerful in ensuring that the code is well-written from the perspective of other code using it.

+

Tested code provides confidence to be able to make changes without breaking things. If we write tests after the code, we’re less likely to write them. Tests written after the code also tend to test the implementation instead of the desired functionality. What we really want is tests written as a specification. With tests as a specification, we can come back later and understand why code was written. Without tests, or with poor tests, we can’t understand why the code is there; if we want to rewrite it, we don’t have the confidence that we’re not missing something. Writing tests first also ensures that we only write the code that is needed to implement the required functionality.

+

I’m not sure why DHH hasn’t “gotten” TDD. I’m not sure if it’s because he’s  a better coder than average, or if he just thinks in a different way than most of us. I think it’s partly because he doesn’t understand TDD, which he admitted might be the case. And I think he’s conflating TDD and unit testing.

+

DHH is influential in the developer community, especially those newer to Ruby and Rails. People listen to what he has to say. I was happy to see almost every other speaker made fun of DHH’s ideas, and most of the crowd knew better. But there will be a lot of others who will hear DHH, respect his opinions, and not give TDD the try that it deserves. And that’s sad, because it will lead to an overall reduction in code quality in the world.

+

Here are some other people’s thoughts on the matter:

+ +

 

+
+ + +
+ +
+ +

+ 2 thoughts on “TDD Is Alive And Well”

+ + +
    +
  1. + +
  2. +
  3. + +
  4. +
+ + + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/02.html b/public/2015/02.html new file mode 100644 index 0000000..ca1e33b --- /dev/null +++ b/public/2015/02.html @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + +February 2015 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Hexagonal Rails Controllers

+ + + +
+

I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+
+ + +
+ +
+
+ +

Resolutions

+ + + +
+

January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2015/02/02/resolutions.html b/public/2015/02/02/resolutions.html new file mode 100644 index 0000000..ac94690 --- /dev/null +++ b/public/2015/02/02/resolutions.html @@ -0,0 +1,303 @@ + + + + + + + + + + + + + + + + +Resolutions – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Resolutions

+ + + +
+

January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/02/02/resolutions/feed b/public/2015/02/02/resolutions/feed new file mode 100644 index 0000000..a0b668a --- /dev/null +++ b/public/2015/02/02/resolutions/feed @@ -0,0 +1,18 @@ + + + Comments on: Resolutions + + http://blog.boochtek.com/2015/02/02/resolutions + Web Development, Ruby on Rails, Open Source + Tue, 28 Jun 2016 22:02:28 +0000 + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/2015/02/23/hexagonal-rails-controllers.html b/public/2015/02/23/hexagonal-rails-controllers.html new file mode 100644 index 0000000..57c6a51 --- /dev/null +++ b/public/2015/02/23/hexagonal-rails-controllers.html @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + +Hexagonal Rails Controllers – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Hexagonal Rails Controllers

+ + + +
+

I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/04.html b/public/2015/04.html new file mode 100644 index 0000000..d28e3ec --- /dev/null +++ b/public/2015/04.html @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + +April 2015 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

The Power of 1%

+ + + +
+

I frequently participate in a podcast called This Agile Life. Recently, a listener asked how much time Agile teams should spend on self improvement. I said 10% to 25%, leaning towards 15% to 20% for most teams. That comes to at least one hour per day, and maybe even more than one day per week.

+

I’m including personal self improvement and team retrospectives in this self-improvement time. This can be as simple as configuring your IDE to make you more efficient, learning to use a tool better, or following up on action items from a retro.

+

That may seem like a lot of time “wasted”. But I think I can justify the cost of all that time.

+

The purpose of spending time on team or self-improvement — the whole point — is to increase our performance and our efficiency. How much improvement can we expect? Can we improve by 1% each week? That doesn’t sound too unreasonable. I think that’s an achievable goal for almost any team, at least on average.

+

Spending 20% of your time to gain 1% doesn’t seem like it’s worth it — until you consider the long term. With compound interest, you’ll be 67% more efficient by the end of a year.1 At that point, you’ll be able to get things done in 59% of the time — saving 41% of the time required at the beginning of the year.2 The following years will show even more progress, as compared to when you started. If 10x programmers exist, continuous improvement is apparently the way to get there.

+

So there’s a pretty good return on investment, even with a small amount of improvement each week. You’ll be significantly more efficient.

+

But efficiency isn’t really what you should aim for. You should aim for effectiveness. You can be efficient in creating the wrong thing. Part of improving should be ensuring that you’re not just building things right, but that you’re building the right things. Build what the customer really needs. Find ways to ask the right questions.

+

Most importantly, find ways to keep improving. It would be a waste of time not to.

+

1: (1.01 ^ 52) – 1
+2: (0.99 ^ 52)

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2015/04/27/one-percent.html b/public/2015/04/27/one-percent.html new file mode 100644 index 0000000..d62a34c --- /dev/null +++ b/public/2015/04/27/one-percent.html @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + +The Power of 1% – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

The Power of 1%

+ + + +
+

I frequently participate in a podcast called This Agile Life. Recently, a listener asked how much time Agile teams should spend on self improvement. I said 10% to 25%, leaning towards 15% to 20% for most teams. That comes to at least one hour per day, and maybe even more than one day per week.

+

I’m including personal self improvement and team retrospectives in this self-improvement time. This can be as simple as configuring your IDE to make you more efficient, learning to use a tool better, or following up on action items from a retro.

+

That may seem like a lot of time “wasted”. But I think I can justify the cost of all that time.

+

The purpose of spending time on team or self-improvement — the whole point — is to increase our performance and our efficiency. How much improvement can we expect? Can we improve by 1% each week? That doesn’t sound too unreasonable. I think that’s an achievable goal for almost any team, at least on average.

+

Spending 20% of your time to gain 1% doesn’t seem like it’s worth it — until you consider the long term. With compound interest, you’ll be 67% more efficient by the end of a year.1 At that point, you’ll be able to get things done in 59% of the time — saving 41% of the time required at the beginning of the year.2 The following years will show even more progress, as compared to when you started. If 10x programmers exist, continuous improvement is apparently the way to get there.

+

So there’s a pretty good return on investment, even with a small amount of improvement each week. You’ll be significantly more efficient.

+

But efficiency isn’t really what you should aim for. You should aim for effectiveness. You can be efficient in creating the wrong thing. Part of improving should be ensuring that you’re not just building things right, but that you’re building the right things. Build what the customer really needs. Find ways to ask the right questions.

+

Most importantly, find ways to keep improving. It would be a waste of time not to.

+

1: (1.01 ^ 52) – 1
+2: (0.99 ^ 52)

+
+ + +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2015/05.html b/public/2015/05.html new file mode 100644 index 0000000..1c364e2 --- /dev/null +++ b/public/2015/05.html @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + +May 2015 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

From Agile To Happiness

+ + + +
+

The Agile Manifesto was written in 2001, as a way to explain the common values amongst several “light-weight” software development methodologies that had come about. The term “Agile” was chosen as a shorthand for those commonalities.

+

Once “Agile” started to show success, we started to see many people use the term to market their products and services, whether or not they really believed in the values and principles of the Agile Manifesto. It’s gotten to the point where some of us don’t see much value in using the term “Agile” any more. Even some of those involved in creating the manifesto have suggested new terms. Dave Thomas suggests “Agility” and Andy Hunt has started working on something called GROWS. Personally, I’m considering going back to the term “Extreme Programming”, even though I’ve incorporated ideas from other Agile methodologies.

+

It recently occurred to me that Agile, when done “right”, is closely aligned with the happiness of the team members. This is really interesting, because it aligns the interests of the employees and the business — a win-win situation. My next thought was that maybe the next step after “Agile” will be a focus on happiness and motivation.

+

I’ve recently been thinking about personal motivation lately, in the context of team practices. According to Daniel Pink’s book Drive, people are motivated by autonomy, mastery, and purpose. I personally add a fourth that can sometimes trump the other three: identity. And of course, happiness can also be motivating — both in the attempt to achieve happiness and in just being happy. (I suspect that happiness is more of a parallel to motivation than a cause though.)

+

There are a couple different ways that happiness can be achieved at work. The traditional way is for work to be a means to an end. In this view, the purpose of your job is to provide the money to live your life (outside of work) the way that you want to live it. There’s nothing wrong with this way of thinking. But for the lucky few, we can work on something that makes us happy in and of itself. That’s generally done by finding a job doing something that we enjoy.

+

But perhaps that’s thinking about things the wrong way. For one, plenty of people who have gone that route are still unhappy at work. I think a lot of that has to do more with the context surrounding the work than the work itself. Maybe you’ve got a lousy manager. Maybe you don’t like the people you work with. Maybe the tools you have to work with are bad. Maybe the processes add a lot of unnecessary tedium.

+

So maybe we need to find ways to be happier at work. Most of the Agile practices seem to make team members happy. For example, replacing a light-weight process for a heavier process always makes me happy. And I typically leave retrospectives in a good mood. So that’s a good starting point. But we should see if we can take the idea further. If we take employee happiness as a core value, where can we go? What kind of practices would we want to add? Please share any ideas in the comments below.

+
+ + +
+ +
+
+ +

When Should We Do TDD?

+ + + +
+

On a recent episode (78) of This Agile Life, my fellow hosts talked about when to do Test-Driven Development (TDD). They all said that you should always do TDD — at least for anything that will go into production; there’s an exception for experimenting or “spiking”.

+

I wasn’t on that episode, but later commented on the topic. (Episode 83 — which wasn’t really all that great.) My take was slightly different. I said that you should do TDD only when the benefits outweigh the costs. Unfortunately, we usually greatly underestimate the benefits. And the costs often seem high at first, because it takes some time to get good at writing automated tests. Not to mention that both the costs and the benefits are usually hard to measure.

+

What is the cost of writing automated tests? I’ve asked this question before and recorded the answers (inasmuch as we have them) in a previous blog entry. TDD costs us about 10-30% in short-term productivity. The benefit that they found was a reduction in bugs by 30-90%, and a decrease in code complexity by about 30%.

+

But what about when the costs are higher than the normal 10 to 30 percent? One good example of this is when there’s no test framework for the situation you’re testing. This might be a new language or framework that you’re working with. More likely it’s a complex API that you have to mock out. So that could increase the cost of automated testing so as to outweigh the benefits — especially on a short project. I could imagine situations where the cost of writing the mocks could eat up more than the project itself.

+

Another case where we might consider skipping testing is when we’re more concerned about time to market than quality. This is almost always a mistake. Your code will almost always last longer than you expect. (Remember Y2K?) And if the code lasts longer than you expect, that means you’ll have to deal with bugs that whole time. But we have to work with the information we have at the time we make our decisions. And sometimes that might tell us that time to market is more important than anything.

+

One final case I can imagine is when a true expert is coding something that they’re very familiar with. I could picture someone like Uncle Bob writing code (in a language that he’s familiar with) without tests just as effectively as I could write code with tests.

+

But these situations should not be common; they’re edge cases. In almost all real-world cases, TDD is the right thing to do. Don’t forget, TDD is also a design discipline — it helps you design a better API. So keep doing TDD. But as with any practice, don’t do it blindly without considering why we’re doing it. Make sure you understand the costs, and whether the benefits outweigh the costs.

+
+ + +
+ +
+
+ +

Good Enough

+ + + +
+

I ran into some former colleagues recently, from a company where I had worked to help transform the team to be more Agile. They’ve gone through some reorganization and management changes recently. One of the guys said that their team culture has helped them maintain quality in the face of those changes. This struck me as odd, since I had considered the work I had done there as somewhat of a disappointment. While I felt I had made a small dent, I didn’t feel like I’d made a true Agile transformation. Much of what I had taught didn’t seem to “stick”.

+

Later that night I thought about why my opinion of the team and his opinion were so different. There are a lot of reasons why an Agile “transformation” could fail. It could be due to lack of support from management. Or even worse, not getting the team members to buy into the ideas — usually due to fear of change. But while those had some effect in this case, they weren’t really the main issues. Now that I’ve had some time and some distance from that team, I’ve gained some clarity on what the real issues were.

+

I think the reason that I think this transformation was a failure was due to the continued pace of change that true Agile requires. Basically the team experienced “change fatigue”, where everything keeps changing and you feel like you can never catch your breath. But the more interesting thing is how our perceptions differed. He seemed to think that the transformation was a success — they’re doing things better than they used to. I view it as more of a failure, because they basically stopped improving — at least at the pace that I was hoping for.

+

I think this difference in mindset is pretty fundamental. My view of Agile involves continuous improvement — always inspecting and adapting our processes. I suppose that means that my preferred flavor of Agile is “Kaizen“. But other people don’t see improvement and change as a constant — they see each change as a means to an end. I’m starting to realize that neither viewpoint is objectively correct. Maybe they’re both fine viewpoints to have. Maybe I shouldn’t be so hard on myself and view that engagement as a failure, especially if my teammates view it as a success. Maybe perfection is the enemy of the good. And maybe I need to learn to be happy with “good enough”.

+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2015/05/04/good-enough.html b/public/2015/05/04/good-enough.html new file mode 100644 index 0000000..2a262c5 --- /dev/null +++ b/public/2015/05/04/good-enough.html @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + +Good Enough – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Good Enough

+ + + +
+

I ran into some former colleagues recently, from a company where I had worked to help transform the team to be more Agile. They’ve gone through some reorganization and management changes recently. One of the guys said that their team culture has helped them maintain quality in the face of those changes. This struck me as odd, since I had considered the work I had done there as somewhat of a disappointment. While I felt I had made a small dent, I didn’t feel like I’d made a true Agile transformation. Much of what I had taught didn’t seem to “stick”.

+

Later that night I thought about why my opinion of the team and his opinion were so different. There are a lot of reasons why an Agile “transformation” could fail. It could be due to lack of support from management. Or even worse, not getting the team members to buy into the ideas — usually due to fear of change. But while those had some effect in this case, they weren’t really the main issues. Now that I’ve had some time and some distance from that team, I’ve gained some clarity on what the real issues were.

+

I think the reason that I think this transformation was a failure was due to the continued pace of change that true Agile requires. Basically the team experienced “change fatigue”, where everything keeps changing and you feel like you can never catch your breath. But the more interesting thing is how our perceptions differed. He seemed to think that the transformation was a success — they’re doing things better than they used to. I view it as more of a failure, because they basically stopped improving — at least at the pace that I was hoping for.

+

I think this difference in mindset is pretty fundamental. My view of Agile involves continuous improvement — always inspecting and adapting our processes. I suppose that means that my preferred flavor of Agile is “Kaizen“. But other people don’t see improvement and change as a constant — they see each change as a means to an end. I’m starting to realize that neither viewpoint is objectively correct. Maybe they’re both fine viewpoints to have. Maybe I shouldn’t be so hard on myself and view that engagement as a failure, especially if my teammates view it as a success. Maybe perfection is the enemy of the good. And maybe I need to learn to be happy with “good enough”.

+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/05/04/good-enough/feed b/public/2015/05/04/good-enough/feed new file mode 100644 index 0000000..2dcb4e0 --- /dev/null +++ b/public/2015/05/04/good-enough/feed @@ -0,0 +1,18 @@ + + + Comments on: Good Enough + + http://blog.boochtek.com/2015/05/04/good-enough + Web Development, Ruby on Rails, Open Source + Sat, 15 Nov 2014 14:48:17 +0000 + hourly + 1 + http://wordpress.org/?v=3.8.1 + + diff --git a/public/2015/05/11/when-tdd.html b/public/2015/05/11/when-tdd.html new file mode 100644 index 0000000..13784ca --- /dev/null +++ b/public/2015/05/11/when-tdd.html @@ -0,0 +1,341 @@ + + + + + + + + + + + + + + + + +When Should We Do TDD? – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

When Should We Do TDD?

+ + + +
+

On a recent episode (78) of This Agile Life, my fellow hosts talked about when to do Test-Driven Development (TDD). They all said that you should always do TDD — at least for anything that will go into production; there’s an exception for experimenting or “spiking”.

+

I wasn’t on that episode, but later commented on the topic. (Episode 83 — which wasn’t really all that great.) My take was slightly different. I said that you should do TDD only when the benefits outweigh the costs. Unfortunately, we usually greatly underestimate the benefits. And the costs often seem high at first, because it takes some time to get good at writing automated tests. Not to mention that both the costs and the benefits are usually hard to measure.

+

What is the cost of writing automated tests? I’ve asked this question before and recorded the answers (inasmuch as we have them) in a previous blog entry. TDD costs us about 10-30% in short-term productivity. The benefit that they found was a reduction in bugs by 30-90%, and a decrease in code complexity by about 30%.

+

But what about when the costs are higher than the normal 10 to 30 percent? One good example of this is when there’s no test framework for the situation you’re testing. This might be a new language or framework that you’re working with. More likely it’s a complex API that you have to mock out. So that could increase the cost of automated testing so as to outweigh the benefits — especially on a short project. I could imagine situations where the cost of writing the mocks could eat up more than the project itself.

+

Another case where we might consider skipping testing is when we’re more concerned about time to market than quality. This is almost always a mistake. Your code will almost always last longer than you expect. (Remember Y2K?) And if the code lasts longer than you expect, that means you’ll have to deal with bugs that whole time. But we have to work with the information we have at the time we make our decisions. And sometimes that might tell us that time to market is more important than anything.

+

One final case I can imagine is when a true expert is coding something that they’re very familiar with. I could picture someone like Uncle Bob writing code (in a language that he’s familiar with) without tests just as effectively as I could write code with tests.

+

But these situations should not be common; they’re edge cases. In almost all real-world cases, TDD is the right thing to do. Don’t forget, TDD is also a design discipline — it helps you design a better API. So keep doing TDD. But as with any practice, don’t do it blindly without considering why we’re doing it. Make sure you understand the costs, and whether the benefits outweigh the costs.

+
+ + +
+ +
+ +

+ 2 thoughts on “When Should We Do TDD?”

+ + +
    +
  1. +
    + + +
    +

    I think it’s important for people to realize that TDD is *really* hard when you’re first learning how to just code in general. In that case, I think it’s useful to let people spike out something and figure out how to do it and then redo it test first. I’ve always had a hard time when people will tell me to do it test-first, and I’m sitting there thinking that I don’t even know how to do it without tests much less test-first. Luckily, I stuck with it, but I know a lot of beginners who got burnt out on that.

    +
    + +
    +
  2. +
  3. + +
  4. +
+ + + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/05/11/when-tdd/feed b/public/2015/05/11/when-tdd/feed new file mode 100644 index 0000000..28ce582 --- /dev/null +++ b/public/2015/05/11/when-tdd/feed @@ -0,0 +1,18 @@ + + + Comments on: When Should We Do TDD? + + http://blog.boochtek.com/2015/05/11/when-tdd + Web Development, Ruby on Rails, Open Source + Sun, 03 May 2015 15:02:55 +0000 + hourly + 1 + http://wordpress.org/?v=3.8.1 + + diff --git a/public/2015/05/18/agile-to-happiness.html b/public/2015/05/18/agile-to-happiness.html new file mode 100644 index 0000000..596ea02 --- /dev/null +++ b/public/2015/05/18/agile-to-happiness.html @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + +From Agile To Happiness – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

From Agile To Happiness

+ + + +
+

The Agile Manifesto was written in 2001, as a way to explain the common values amongst several “light-weight” software development methodologies that had come about. The term “Agile” was chosen as a shorthand for those commonalities.

+

Once “Agile” started to show success, we started to see many people use the term to market their products and services, whether or not they really believed in the values and principles of the Agile Manifesto. It’s gotten to the point where some of us don’t see much value in using the term “Agile” any more. Even some of those involved in creating the manifesto have suggested new terms. Dave Thomas suggests “Agility” and Andy Hunt has started working on something called GROWS. Personally, I’m considering going back to the term “Extreme Programming”, even though I’ve incorporated ideas from other Agile methodologies.

+

It recently occurred to me that Agile, when done “right”, is closely aligned with the happiness of the team members. This is really interesting, because it aligns the interests of the employees and the business — a win-win situation. My next thought was that maybe the next step after “Agile” will be a focus on happiness and motivation.

+

I’ve recently been thinking about personal motivation lately, in the context of team practices. According to Daniel Pink’s book Drive, people are motivated by autonomy, mastery, and purpose. I personally add a fourth that can sometimes trump the other three: identity. And of course, happiness can also be motivating — both in the attempt to achieve happiness and in just being happy. (I suspect that happiness is more of a parallel to motivation than a cause though.)

+

There are a couple different ways that happiness can be achieved at work. The traditional way is for work to be a means to an end. In this view, the purpose of your job is to provide the money to live your life (outside of work) the way that you want to live it. There’s nothing wrong with this way of thinking. But for the lucky few, we can work on something that makes us happy in and of itself. That’s generally done by finding a job doing something that we enjoy.

+

But perhaps that’s thinking about things the wrong way. For one, plenty of people who have gone that route are still unhappy at work. I think a lot of that has to do more with the context surrounding the work than the work itself. Maybe you’ve got a lousy manager. Maybe you don’t like the people you work with. Maybe the tools you have to work with are bad. Maybe the processes add a lot of unnecessary tedium.

+

So maybe we need to find ways to be happier at work. Most of the Agile practices seem to make team members happy. For example, replacing a light-weight process for a heavier process always makes me happy. And I typically leave retrospectives in a good mood. So that’s a good starting point. But we should see if we can take the idea further. If we take employee happiness as a core value, where can we go? What kind of practices would we want to add? Please share any ideas in the comments below.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/06.html b/public/2015/06.html new file mode 100644 index 0000000..84fc9b1 --- /dev/null +++ b/public/2015/06.html @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + +June 2015 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Not Quite Callbacks

+ + + +
+

I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+
+ + +
+ +
+
+ +

Architectural Thoughts

+ + + +
+

I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2015/06/01/architectural-thoughts.html b/public/2015/06/01/architectural-thoughts.html new file mode 100644 index 0000000..8976a33 --- /dev/null +++ b/public/2015/06/01/architectural-thoughts.html @@ -0,0 +1,294 @@ + + + + + + + + + + + + + + + + +Architectural Thoughts – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Architectural Thoughts

+ + + +
+

I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/06/22/not-quite-callbacks.html b/public/2015/06/22/not-quite-callbacks.html new file mode 100644 index 0000000..4218732 --- /dev/null +++ b/public/2015/06/22/not-quite-callbacks.html @@ -0,0 +1,305 @@ + + + + + + + + + + + + + + + + +Not Quite Callbacks – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Not Quite Callbacks

+ + + +
+

I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/08.html b/public/2015/08.html new file mode 100644 index 0000000..bc10fb0 --- /dev/null +++ b/public/2015/08.html @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + +August 2015 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Potential F5 Vulnerability

+ + + +
+

It all started with an email about a WebInspect report. It listed a buffer overflow, which we had marked as a false positive. I read the WebInspect report carefully, and found a note at the bottom that said you could test manually to confirm whether it was a false positive or not. Unfortunately, the manual test listed had a few problems. First, it jammed the lines together, without the proper line-breaks. Second, it assumed the site was using HTTP, not HTTPS, so used telnet. Third, it was testing against a page that didn’t exist, giving a 404. Keeping those in mind, I tried the manual test using the openssl s_client command:

+
openssl s_client -quiet -connect mysite.example.com:443
+POST /login HTTP/1.1
+Host: mysite.example.com
+Transfer-Encoding: chunked
+
+c00000000
+

The test terminated immediately. The report said that this meant that the server was vulnerable to a buffer overflow and arbitrary code execution.

+

At first, we thought this was caused by Apache or Tomcat, or possibly the application code. But the reported vulnerability was an Apache CVE from 2002 (CVE-2002-0392), fixed by vendors long ago. After a while, we realized that if we hit the servers directly, we did not get the indication of a vulnerability. If we hit the site through the F5 VIP, we saw the immediate termination of the connection. The issue is with handling of HTTP chunked encoding. Nginx had a similar issue in 2013 (CVE-2013-2028).

+

So we turned our attention to the F5 load balancers. We were able to confirm that other sites using F5 load balancers were exhibiting the same behavior. We also confirmed that WebInspect run against the servers directly did not show the issue (even as a false positive). We reported the issue to F5, and they are looking into it.

+

I’m disclosing this publicly now for a few reasons. First, I’m not a security researcher, and almost nobody follows my blog — especially people looking for security issues that could be exploited. Second, I’ve not developed a payload, so I have no idea whether this is exploitable. But at this point, it’s merely a potential vulnerability. I’m not sure I’ll even spend the time to research and create a payload to prove the vulnerability. If I do, I’ll be more careful with the disclosure.

+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2015/08/31/potential-f5-vulnerability.html b/public/2015/08/31/potential-f5-vulnerability.html new file mode 100644 index 0000000..bfd8c43 --- /dev/null +++ b/public/2015/08/31/potential-f5-vulnerability.html @@ -0,0 +1,293 @@ + + + + + + + + + + + + + + + + +Potential F5 Vulnerability – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Potential F5 Vulnerability

+ + + +
+

It all started with an email about a WebInspect report. It listed a buffer overflow, which we had marked as a false positive. I read the WebInspect report carefully, and found a note at the bottom that said you could test manually to confirm whether it was a false positive or not. Unfortunately, the manual test listed had a few problems. First, it jammed the lines together, without the proper line-breaks. Second, it assumed the site was using HTTP, not HTTPS, so used telnet. Third, it was testing against a page that didn’t exist, giving a 404. Keeping those in mind, I tried the manual test using the openssl s_client command:

+
openssl s_client -quiet -connect mysite.example.com:443
+POST /login HTTP/1.1
+Host: mysite.example.com
+Transfer-Encoding: chunked
+
+c00000000
+

The test terminated immediately. The report said that this meant that the server was vulnerable to a buffer overflow and arbitrary code execution.

+

At first, we thought this was caused by Apache or Tomcat, or possibly the application code. But the reported vulnerability was an Apache CVE from 2002 (CVE-2002-0392), fixed by vendors long ago. After a while, we realized that if we hit the servers directly, we did not get the indication of a vulnerability. If we hit the site through the F5 VIP, we saw the immediate termination of the connection. The issue is with handling of HTTP chunked encoding. Nginx had a similar issue in 2013 (CVE-2013-2028).

+

So we turned our attention to the F5 load balancers. We were able to confirm that other sites using F5 load balancers were exhibiting the same behavior. We also confirmed that WebInspect run against the servers directly did not show the issue (even as a false positive). We reported the issue to F5, and they are looking into it.

+

I’m disclosing this publicly now for a few reasons. First, I’m not a security researcher, and almost nobody follows my blog — especially people looking for security issues that could be exploited. Second, I’ve not developed a payload, so I have no idea whether this is exploitable. But at this point, it’s merely a potential vulnerability. I’m not sure I’ll even spend the time to research and create a payload to prove the vulnerability. If I do, I’ll be more careful with the disclosure.

+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/09.html b/public/2015/09.html new file mode 100644 index 0000000..3111391 --- /dev/null +++ b/public/2015/09.html @@ -0,0 +1,557 @@ + + + + + + + + + + + + + + + +September 2015 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

The Problem With Estimates

+ + + +
+
+
+

I’m a big proponent of Agile (mostly XP; I’m mostly anti-Scrum) and I’ve contributed some to the #noestimates “movement”.

+

I don’t really mean that nobody should ever estimate anything. I mean that I’ve never seen useful (fine-grained) estimates anywhere. Here are some of the problems with estimates that I’ve seen frequently:

+
    +
  1. We’re not good at estimating how long things will take. We’re usually optimistic about how quickly we can get things done, and almost always miss thinking about things that will take more time. I’ve never seen a case where a project is completed more quickly than estimated. I’ve only rarely seen fine-grained (story-level) tasks completed more quickly than estimated.
  2. +
  3. Management asks for estimates and then treats them as deadlines. The team then learns to inflate their estimates. Then management learns to reduce the estimates they’re given. Given fudge factors in each direction, the estimate no longer has much reliability. Even if you’re using story points, the point inflation/deflation leads to less consistency and therefore reduced reliability.
  4. +
  5. Estimates that are given are negotiated down, or simply reduced. This leads to the question why you’d ask for an estimate and not take the answer provided. If you’re not going to listen to the answer, why are you asking the question? This is probably the craziest one on the list — given my first point, increasing an estimate would make sense. Reducing the estimates is just magical wishful thinking.
  6. +
  7. Plans change and work is added, but the deadline (presumably based on the estimates) is not changed to correspond with the extra work involved. So again, you’re not actually even using the estimates that were given.
  8. +
  9. Management dictates deadlines arbitrarily, without speaking to the people who will be doing the work. Spending time estimating how long each task will take when the deadline is already set is completely pointless.
  10. +
  11. Almost every deadline is complete bullshit, based on nothing. Often the excuse is that marketing needs to know when something will come out, so that they can let people know about it. Why they need to know the exact release date way in advance, I’ve never been able to figure out. Many people intuitively know that the deadlines are bullshit, and will likely be allowed to slip. The only exception to bullshit deadlines I’ve come across are regulatory deadlines. (I know there are a few other exceptions out there.)
  12. +
  13. Estimation at a fine-grained level isn’t necessary. Many Agile teams estimate using story points, and determine a conversion from story points to time based on previous empirical data. This is fine, except that the time spent estimating the story is wasted time — counting the number of stories almost always gives the same predictive power. Teams tend to get better at breaking up stories over time, so that they’re more consistent in size, so this becomes more likely over time.
  14. +
  15. The ultimate purpose of an estimate is to evaluate whether the proposed work will be profitable, and therefore worth doing. Or to compare the ROI (return on investment) between alternative projects. But to know that, you’ll have to know what value that work will provide. I don’t believe I’ve ever seen that done — at least not at a fine-grained level. Usually by the time you’re asked to estimate, the project has already gotten approval to proceed.
  16. +
+

I’ll note that most of these pit management against the team, instead of working together toward a common cause. Most of the practices also lead to seriously demoralizing the team. And most of the time, the estimates aren’t really even taken into account very much.

+

My advice is to first understand the value of a project before you consider estimating the costs. The estimation at this point will be very rough, so make sure that you have a very wide margin between the expected value and the rough estimate of the cost. If you’re pretty certain of the expected value, I’d probably want to make sure I could still be profitable even if it took 3 or 4 times as long to complete as the rough estimate. And if there’s uncertainty in the expected value, much more.

+

Another way to mitigate the risk of throwing money at something that’s not going to have positive ROI is to reduce the feedback loop. Order the work so that the tasks are ranked in order of value to the customer. (Realistically, you’ll have dependencies of tasks to worry about, and should consider effort involved too.) So work on the most valuable feature first — get that out into production as soon as possible. Once that’s done, you can assess if your ROI is positive or not. Keep iterating in this fashion, working on the features that will provide the most value first. Keep assessing your ROI, and stop when the ROI is no longer worth it, compared to other projects the team could be working on.

+

At a fine-grained level, if you’re using story points, I’d ask you to do the math to see if just counting the stories would be as effective at predicting how much will be done over time as using the story points. If so, you can save the time the team spends on estimating stories. I’d still recommend spending time talking about stories so that everyone has a shared understanding of what needs to be done, and to break stories up into a smaller, more manageable size — with one acceptance criteria per story. Also take a look to see if empirical average cycle time (how long it takes a single story to move from start to finish) might provide you the predictive power just as well as estimates. (I.e. is it bandwidth or latency that really provides the predictive power you’re looking for?)

+

And don’t forget Hofstadter’s Law: It always takes longer than you expect, even when you take into account Hofstadter’s Law.

+
+
+
+
+
+
+ + +
+ +
+
+ +

Website Checklist

+ + + +
+

While creating a web page isn’t too difficult, there are a lot of moving parts involved in creating an effective website. We’ve written up this checklist as a guide for anyone who has decided that they need a website.

+

This list is for a basic static website. A web application will require significantly more effort. We’re working on a separate checklist for web apps.

+

Overview

+
    +
  • Determine your goals
  • +
  • Develop a business plan
  • +
  • Register a domain name
  • +
  • Set up DNS hosting
  • +
  • Set up web hosting
  • +
  • Design and develop the site
  • +
  • Deploy and test the site
  • +
  • Market your site
  • +
  • Set up additional services
  • +
  • Maintenance
  • +
+

Goals

+
    +
  • What do you want to achieve from having the site?
  • +
  • What “call to action” do you want visitors to take?
  • +
  • How will you measure success?
  • +
  • What will be the focus of the site? +
      +
    • Info (“brochure”) for an existing business
    • +
    • Blogging
    • +
    • Sales
    • +
    • Community
    • +
    • Web app
    • +
    • Mobile app
    • +
    +
  • +
+

Business Plan

+
    +
  • Who is your target audience?
  • +
  • Marketing +
      +
    • How will you get them to come to your site?
    • +
    • How will you get them to buy something?
    • +
    +
  • +
  • Who is your competition? +
      +
    • How do you compare to them?
    • +
    • How do you differentiate from them?
    • +
    • What is your niche?
    • +
    • What makes your site better than theirs?
    • +
    +
  • +
  • How will you make money? +
      +
    • Bringing people into brick and mortar business
    • +
    • Advertising
    • +
    • Periodic billing/subscriptions
    • +
    • Selling goods/services
    • +
    • Get bought out
    • +
    +
  • +
  • Pricing +
      +
    • Aim high — it’s easier to lower prices than raise them
    • +
    • Tiered pricing often makes sense; most people pick the middle tier
    • +
    +
  • +
  • What will it take to have a positive ROI?
  • +
+

Domain Name

+

You’ll probably want your own domain name.

+
    +
  • Think of a name +
      +
    • Stick with a .com name if possible; don’t use .biz
    • +
    • Some other top-level domains come into fashion on occasion +
        +
      • .io is pretty popular right now
      • +
      +
    • +
    +
  • +
  • Check availability of the name
  • +
  • Keep checking names, until you find a good name that is available
  • +
  • Register the name with a respectable registrar +
      +
    • DNS Registrars run from $10-$35/yr
    • +
    • DO NOT allow your web host provider to own/control your name
    • +
    • You may want to grab the .net, .org, and other versions of the same name
    • +
    • Multiple-year registration is cheaper, but it’s easier to forget how to renew it
    • +
    +
  • +
  • Your registrar will likely point your domain to an “under construction” page initially
  • +
  • DO NOT LOSE YOUR NAME! +
      +
    • Spammers and pornographers will take it over if your registration lapses
    • +
    • Make sure your contact info (especially email address) is up-to-date
    • +
    +
  • +
  • Beware scams (usually by US mail) trying to get you to renew with a different registrar
  • +
  • Note that the email address you register with will get spammed +
      +
    • Some registrars provide some protection for a fee
    • +
    +
  • +
+

DNS Hosting

+

You’ll need to have someone take care of the servers that tell the world what server addresses your domain name corresponds to.

+
    +
  • Find a DNS hosting provider +
      +
    • We like DNSimple; they also do domain registration
    • +
    +
  • +
  • Provide the name servers to your DNS registrar
  • +
+

Web Hosting

+

You’ll need servers to host your web site on. There are a lot of options available, from virtual hosts (a Linux server where you control everything) to application-specific services.

+
    +
  • Talk to your developer or designer first! +
      +
    • Web host can significantly restrain the development environment
    • +
    +
  • +
  • Cost +
      +
    • $10 to $500 / month is typical
    • +
    +
  • +
  • Bandwidth +
      +
    • Number of users × how often they use it × average “page” size
    • +
    • What happens if you go over?
    • +
    +
  • +
  • Up-time +
      +
    • What kind of down-time can the site sustain?
    • +
    • Higher guaranteed uptime costs more
    • +
    • What if the specified uptime is not met?
    • +
    +
  • +
  • Development environment +
      +
    • What programming languages are installed?
    • +
    • What databases are installed?
    • +
    • What libraries are installed?
    • +
    • What if other libraries are required?
    • +
    +
  • +
  • Shared/dedicated/virtual hosting +
      +
    • Shared means others are using the same machine, with security implications
    • +
    • Dedicated is expensive, but you “own” the whole machine
    • +
    • Virtual is somewhere in between +
        +
      • You have a “slice” of a machine dedicated to your site
      • +
      +
    • +
    +
  • +
  • How responsive is the host to problems and requests?
  • +
  • Backups +
      +
    • What do they back up?
    • +
    • How often do they back up?
    • +
    • How can files be restored?
    • +
    +
  • +
+

Design and Development

+

Designing and developing the site can vary from picking an existing template and adding content, to developing a full web application.

+
    +
  • Cost ($30 – $300 / hr)
  • +
  • Project management +
      +
    • Story/task management
    • +
    +
  • +
  • Revision control +
      +
    • Ensures changes can be rolled back quickly
    • +
    +
  • +
  • Functionality +
      +
    • What does the site need to do?
    • +
    +
  • +
  • Usability +
      +
    • How easy is it to use each page?
    • +
    • Is it easy to navigate the site to find what you’re looking for?
    • +
    • Check for broken links
    • +
    • Check for ADA/508 compliance
    • +
    • Spell checking and grammar checking
    • +
    +
  • +
+

Deploying and Testing

+
    +
  • Can updates be deployed quickly? +
      +
    • Deploy early and often, so it’s not such a big deal, and it becomes routine
    • +
    +
  • +
  • Consider a staging and/or beta site +
      +
    • Test everything thoroughly on staging site before deploying to production
    • +
    • Staging site should (mostly) use the same code as production
    • +
    • Staging/test site should not process credit cards, etc.
    • +
    +
  • +
  • Automated testing +
      +
    • Prevents regressions
    • +
    +
  • +
  • Exploratory testing +
      +
    • See how things work
    • +
    • See if you can break things
    • +
    +
  • +
  • Security testing +
      +
    • Penetration testing
    • +
    +
  • +
  • Performance +
      +
    • Load testing
    • +
    +
  • +
  • Beta testers
  • +
+

Marketing

+
    +
  • Search engine “optimization” (SEO) +
      +
    • Good design, good URLs, and well-written HTML should cover most of this
    • +
    • Submit site to search engines
    • +
    • Use robots.txt and site maps
    • +
    +
  • +
  • Directories
  • +
  • PR sites
  • +
  • Targeted groups
  • +
  • DO NOT send out spam
  • +
+

Additional Services

+

What other services do you need, besides just the web site?

+
    +
  • Email
  • +
  • Blog
  • +
  • Wiki
  • +
  • File storage
  • +
  • Forums
  • +
  • Authentication
  • +
  • Customer interaction +
      +
    • CRM
    • +
    • Feedback
    • +
    • Bug tracking
    • +
    • Customer Q&A (StackExchange)
    • +
    • Follow-up emails to customers to offer assistance
    • +
    +
  • +
+

Maintenance

+

Over the lifetime of the site, you’ll likely pay more in maintenance costs than the upfront costs.

+
    +
  • Responding to user emails
  • +
  • Requests for info
  • +
  • Feedback about the site
  • +
  • Password resets?
  • +
  • Tracking bug reports and feature requests
  • +
  • Site improvements
  • +
  • Additional content
  • +
  • Moderation of user content +
      +
    • Spam removal
    • +
    +
  • +
  • Log analysis +
      +
    • Google Analytics
    • +
    +
  • +
  • Assessing advertising effectiveness
  • +
  • Analysis of revenues/profitability
  • +
  • Upgrades +
      +
    • New/improved functionality
    • +
    • Bug fixes
    • +
    • Upgraded infrastructure
    • +
    +
  • +
  • Down-time +
      +
    • Web host
    • +
    • Upgrades
    • +
    • Accidents
    • +
    • Bugs
    • +
    +
  • +
  • Backups +
      +
    • Testing restoring from backups
    • +
    +
  • +
  • Payments for services +
      +
    • Domain name registration – DO NOT LOSE YOUR NAME!
    • +
    • Web hosting
    • +
    • Marketing/advertising
    • +
    +
  • +
+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2015/09/08/website-checklist-2.html b/public/2015/09/08/website-checklist-2.html new file mode 100644 index 0000000..2df2278 --- /dev/null +++ b/public/2015/09/08/website-checklist-2.html @@ -0,0 +1,597 @@ + + + + + + + + + + + + + + + + +Website Checklist – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Website Checklist

+ + + +
+

While creating a web page isn’t too difficult, there are a lot of moving parts involved in creating an effective website. We’ve written up this checklist as a guide for anyone who has decided that they need a website.

+

This list is for a basic static website. A web application will require significantly more effort. We’re working on a separate checklist for web apps.

+

Overview

+
    +
  • Determine your goals
  • +
  • Develop a business plan
  • +
  • Register a domain name
  • +
  • Set up DNS hosting
  • +
  • Set up web hosting
  • +
  • Design and develop the site
  • +
  • Deploy and test the site
  • +
  • Market your site
  • +
  • Set up additional services
  • +
  • Maintenance
  • +
+

Goals

+
    +
  • What do you want to achieve from having the site?
  • +
  • What “call to action” do you want visitors to take?
  • +
  • How will you measure success?
  • +
  • What will be the focus of the site? +
      +
    • Info (“brochure”) for an existing business
    • +
    • Blogging
    • +
    • Sales
    • +
    • Community
    • +
    • Web app
    • +
    • Mobile app
    • +
    +
  • +
+

Business Plan

+
    +
  • Who is your target audience?
  • +
  • Marketing +
      +
    • How will you get them to come to your site?
    • +
    • How will you get them to buy something?
    • +
    +
  • +
  • Who is your competition? +
      +
    • How do you compare to them?
    • +
    • How do you differentiate from them?
    • +
    • What is your niche?
    • +
    • What makes your site better than theirs?
    • +
    +
  • +
  • How will you make money? +
      +
    • Bringing people into brick and mortar business
    • +
    • Advertising
    • +
    • Periodic billing/subscriptions
    • +
    • Selling goods/services
    • +
    • Get bought out
    • +
    +
  • +
  • Pricing +
      +
    • Aim high — it’s easier to lower prices than raise them
    • +
    • Tiered pricing often makes sense; most people pick the middle tier
    • +
    +
  • +
  • What will it take to have a positive ROI?
  • +
+

Domain Name

+

You’ll probably want your own domain name.

+
    +
  • Think of a name +
      +
    • Stick with a .com name if possible; don’t use .biz
    • +
    • Some other top-level domains come into fashion on occasion +
        +
      • .io is pretty popular right now
      • +
      +
    • +
    +
  • +
  • Check availability of the name
  • +
  • Keep checking names, until you find a good name that is available
  • +
  • Register the name with a respectable registrar +
      +
    • DNS Registrars run from $10-$35/yr
    • +
    • DO NOT allow your web host provider to own/control your name
    • +
    • You may want to grab the .net, .org, and other versions of the same name
    • +
    • Multiple-year registration is cheaper, but it’s easier to forget how to renew it
    • +
    +
  • +
  • Your registrar will likely point your domain to an “under construction” page initially
  • +
  • DO NOT LOSE YOUR NAME! +
      +
    • Spammers and pornographers will take it over if your registration lapses
    • +
    • Make sure your contact info (especially email address) is up-to-date
    • +
    +
  • +
  • Beware scams (usually by US mail) trying to get you to renew with a different registrar
  • +
  • Note that the email address you register with will get spammed +
      +
    • Some registrars provide some protection for a fee
    • +
    +
  • +
+

DNS Hosting

+

You’ll need to have someone take care of the servers that tell the world what server addresses your domain name corresponds to.

+
    +
  • Find a DNS hosting provider +
      +
    • We like DNSimple; they also do domain registration
    • +
    +
  • +
  • Provide the name servers to your DNS registrar
  • +
+

Web Hosting

+

You’ll need servers to host your web site on. There are a lot of options available, from virtual hosts (a Linux server where you control everything) to application-specific services.

+
    +
  • Talk to your developer or designer first! +
      +
    • Web host can significantly restrain the development environment
    • +
    +
  • +
  • Cost +
      +
    • $10 to $500 / month is typical
    • +
    +
  • +
  • Bandwidth +
      +
    • Number of users × how often they use it × average “page” size
    • +
    • What happens if you go over?
    • +
    +
  • +
  • Up-time +
      +
    • What kind of down-time can the site sustain?
    • +
    • Higher guaranteed uptime costs more
    • +
    • What if the specified uptime is not met?
    • +
    +
  • +
  • Development environment +
      +
    • What programming languages are installed?
    • +
    • What databases are installed?
    • +
    • What libraries are installed?
    • +
    • What if other libraries are required?
    • +
    +
  • +
  • Shared/dedicated/virtual hosting +
      +
    • Shared means others are using the same machine, with security implications
    • +
    • Dedicated is expensive, but you “own” the whole machine
    • +
    • Virtual is somewhere in between +
        +
      • You have a “slice” of a machine dedicated to your site
      • +
      +
    • +
    +
  • +
  • How responsive is the host to problems and requests?
  • +
  • Backups +
      +
    • What do they back up?
    • +
    • How often do they back up?
    • +
    • How can files be restored?
    • +
    +
  • +
+

Design and Development

+

Designing and developing the site can vary from picking an existing template and adding content, to developing a full web application.

+
    +
  • Cost ($30 – $300 / hr)
  • +
  • Project management +
      +
    • Story/task management
    • +
    +
  • +
  • Revision control +
      +
    • Ensures changes can be rolled back quickly
    • +
    +
  • +
  • Functionality +
      +
    • What does the site need to do?
    • +
    +
  • +
  • Usability +
      +
    • How easy is it to use each page?
    • +
    • Is it easy to navigate the site to find what you’re looking for?
    • +
    • Check for broken links
    • +
    • Check for ADA/508 compliance
    • +
    • Spell checking and grammar checking
    • +
    +
  • +
+

Deploying and Testing

+
    +
  • Can updates be deployed quickly? +
      +
    • Deploy early and often, so it’s not such a big deal, and it becomes routine
    • +
    +
  • +
  • Consider a staging and/or beta site +
      +
    • Test everything thoroughly on staging site before deploying to production
    • +
    • Staging site should (mostly) use the same code as production
    • +
    • Staging/test site should not process credit cards, etc.
    • +
    +
  • +
  • Automated testing +
      +
    • Prevents regressions
    • +
    +
  • +
  • Exploratory testing +
      +
    • See how things work
    • +
    • See if you can break things
    • +
    +
  • +
  • Security testing +
      +
    • Penetration testing
    • +
    +
  • +
  • Performance +
      +
    • Load testing
    • +
    +
  • +
  • Beta testers
  • +
+

Marketing

+
    +
  • Search engine “optimization” (SEO) +
      +
    • Good design, good URLs, and well-written HTML should cover most of this
    • +
    • Submit site to search engines
    • +
    • Use robots.txt and site maps
    • +
    +
  • +
  • Directories
  • +
  • PR sites
  • +
  • Targeted groups
  • +
  • DO NOT send out spam
  • +
+

Additional Services

+

What other services do you need, besides just the web site?

+
    +
  • Email
  • +
  • Blog
  • +
  • Wiki
  • +
  • File storage
  • +
  • Forums
  • +
  • Authentication
  • +
  • Customer interaction +
      +
    • CRM
    • +
    • Feedback
    • +
    • Bug tracking
    • +
    • Customer Q&A (StackExchange)
    • +
    • Follow-up emails to customers to offer assistance
    • +
    +
  • +
+

Maintenance

+

Over the lifetime of the site, you’ll likely pay more in maintenance costs than the upfront costs.

+
    +
  • Responding to user emails
  • +
  • Requests for info
  • +
  • Feedback about the site
  • +
  • Password resets?
  • +
  • Tracking bug reports and feature requests
  • +
  • Site improvements
  • +
  • Additional content
  • +
  • Moderation of user content +
      +
    • Spam removal
    • +
    +
  • +
  • Log analysis +
      +
    • Google Analytics
    • +
    +
  • +
  • Assessing advertising effectiveness
  • +
  • Analysis of revenues/profitability
  • +
  • Upgrades +
      +
    • New/improved functionality
    • +
    • Bug fixes
    • +
    • Upgraded infrastructure
    • +
    +
  • +
  • Down-time +
      +
    • Web host
    • +
    • Upgrades
    • +
    • Accidents
    • +
    • Bugs
    • +
    +
  • +
  • Backups +
      +
    • Testing restoring from backups
    • +
    +
  • +
  • Payments for services +
      +
    • Domain name registration – DO NOT LOSE YOUR NAME!
    • +
    • Web hosting
    • +
    • Marketing/advertising
    • +
    +
  • +
+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/09/28/no-estimates.html b/public/2015/09/28/no-estimates.html new file mode 100644 index 0000000..3e4f32c --- /dev/null +++ b/public/2015/09/28/no-estimates.html @@ -0,0 +1,361 @@ + + + + + + + + + + + + + + + + +The Problem With Estimates – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

The Problem With Estimates

+ + + +
+
+
+

I’m a big proponent of Agile (mostly XP; I’m mostly anti-Scrum) and I’ve contributed some to the #noestimates “movement”.

+

I don’t really mean that nobody should ever estimate anything. I mean that I’ve never seen useful (fine-grained) estimates anywhere. Here are some of the problems with estimates that I’ve seen frequently:

+
    +
  1. We’re not good at estimating how long things will take. We’re usually optimistic about how quickly we can get things done, and almost always miss thinking about things that will take more time. I’ve never seen a case where a project is completed more quickly than estimated. I’ve only rarely seen fine-grained (story-level) tasks completed more quickly than estimated.
  2. +
  3. Management asks for estimates and then treats them as deadlines. The team then learns to inflate their estimates. Then management learns to reduce the estimates they’re given. Given fudge factors in each direction, the estimate no longer has much reliability. Even if you’re using story points, the point inflation/deflation leads to less consistency and therefore reduced reliability.
  4. +
  5. Estimates that are given are negotiated down, or simply reduced. This leads to the question why you’d ask for an estimate and not take the answer provided. If you’re not going to listen to the answer, why are you asking the question? This is probably the craziest one on the list — given my first point, increasing an estimate would make sense. Reducing the estimates is just magical wishful thinking.
  6. +
  7. Plans change and work is added, but the deadline (presumably based on the estimates) is not changed to correspond with the extra work involved. So again, you’re not actually even using the estimates that were given.
  8. +
  9. Management dictates deadlines arbitrarily, without speaking to the people who will be doing the work. Spending time estimating how long each task will take when the deadline is already set is completely pointless.
  10. +
  11. Almost every deadline is complete bullshit, based on nothing. Often the excuse is that marketing needs to know when something will come out, so that they can let people know about it. Why they need to know the exact release date way in advance, I’ve never been able to figure out. Many people intuitively know that the deadlines are bullshit, and will likely be allowed to slip. The only exception to bullshit deadlines I’ve come across are regulatory deadlines. (I know there are a few other exceptions out there.)
  12. +
  13. Estimation at a fine-grained level isn’t necessary. Many Agile teams estimate using story points, and determine a conversion from story points to time based on previous empirical data. This is fine, except that the time spent estimating the story is wasted time — counting the number of stories almost always gives the same predictive power. Teams tend to get better at breaking up stories over time, so that they’re more consistent in size, so this becomes more likely over time.
  14. +
  15. The ultimate purpose of an estimate is to evaluate whether the proposed work will be profitable, and therefore worth doing. Or to compare the ROI (return on investment) between alternative projects. But to know that, you’ll have to know what value that work will provide. I don’t believe I’ve ever seen that done — at least not at a fine-grained level. Usually by the time you’re asked to estimate, the project has already gotten approval to proceed.
  16. +
+

I’ll note that most of these pit management against the team, instead of working together toward a common cause. Most of the practices also lead to seriously demoralizing the team. And most of the time, the estimates aren’t really even taken into account very much.

+

My advice is to first understand the value of a project before you consider estimating the costs. The estimation at this point will be very rough, so make sure that you have a very wide margin between the expected value and the rough estimate of the cost. If you’re pretty certain of the expected value, I’d probably want to make sure I could still be profitable even if it took 3 or 4 times as long to complete as the rough estimate. And if there’s uncertainty in the expected value, much more.

+

Another way to mitigate the risk of throwing money at something that’s not going to have positive ROI is to reduce the feedback loop. Order the work so that the tasks are ranked in order of value to the customer. (Realistically, you’ll have dependencies of tasks to worry about, and should consider effort involved too.) So work on the most valuable feature first — get that out into production as soon as possible. Once that’s done, you can assess if your ROI is positive or not. Keep iterating in this fashion, working on the features that will provide the most value first. Keep assessing your ROI, and stop when the ROI is no longer worth it, compared to other projects the team could be working on.

+

At a fine-grained level, if you’re using story points, I’d ask you to do the math to see if just counting the stories would be as effective at predicting how much will be done over time as using the story points. If so, you can save the time the team spends on estimating stories. I’d still recommend spending time talking about stories so that everyone has a shared understanding of what needs to be done, and to break stories up into a smaller, more manageable size — with one acceptance criteria per story. Also take a look to see if empirical average cycle time (how long it takes a single story to move from start to finish) might provide you the predictive power just as well as estimates. (I.e. is it bandwidth or latency that really provides the predictive power you’re looking for?)

+

And don’t forget Hofstadter’s Law: It always takes longer than you expect, even when you take into account Hofstadter’s Law.

+
+
+
+
+
+
+ + +
+ +
+ +

+ 2 thoughts on “The Problem With Estimates”

+ + +
    +
  1. + +
  2. +
  3. + +
  4. +
+ + + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/09/28/no-estimates/feed b/public/2015/09/28/no-estimates/feed new file mode 100644 index 0000000..a02ee1c --- /dev/null +++ b/public/2015/09/28/no-estimates/feed @@ -0,0 +1,18 @@ + + + Comments on: The Problem With Estimates + + http://blog.boochtek.com/2015/09/28/no-estimates + Web Development, Ruby on Rails, Open Source + Wed, 13 May 2015 03:10:44 +0000 + hourly + 1 + http://wordpress.org/?v=3.8.1 + + diff --git a/public/2015/11.html b/public/2015/11.html new file mode 100644 index 0000000..459daee --- /dev/null +++ b/public/2015/11.html @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + +November 2015 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Show and Tell

+ + + +
+

I’m wrapping up my current consulting gig at Mercy in December, and starting a new gig at CenturyLink. I’m a software developer, but that’s only a part of what I do. What I really do is join a team and help them improve the way they work — both their processes and their technical skills.

+

I think this is a key differentiator for me as a consultant. Most consultants (and Agile coaches) come in and tell people what to do. I don’t like to just tell people what to do. I’d much prefer to work side-by-side with them, getting a better understanding of what their challenges are. Once I have a better understanding of the challenges, I’m able to better brainstorm some ideas to try. Then we can experiment to see what will work and what won’t.

+

Instead of telling people what to do, I show them how. Most people learn better from seeing than from hearing. They also learn better if you explain how and why, not just what. So showing them how to do something is more effective than telling them. By showing and doing, you can also set a good example. This is especially important when collaboration is a large part of what needs to be improved.

+

I’ve found that this style of consulting is more highly respected by everyone. I build trust with developers by working closely with them. Managers like to keep me around once they see how effective these methods can be, so the gigs I take on tend to last relatively long.

+

The biggest problem I have is explaining how this works. I don’t really know what to put on my résumé. Sometimes I call myself an Agile practitioner, and sometimes an Agile player/coach. But those aren’t terribly satisfying descriptions. I’m considering actually putting “I help teams improve the way they work — both their processes and their technical skills” on the résumé. But that seems awkward, and misses the show versus tell part. I’d be open to any suggestions.

+

 

+
+ + +
+ +
+
+ +

Happiness Retrospective

+ + + +
+

I facilitated a retrospective today; it was one of the best retros I’ve ever been involved with. I figured out what activities I wanted to do earlier in the morning. They were really quite simple. I wanted to focus on happiness.

+

How happy are you at work?

+

I started with two questions that I’ve used with teams before, to some success (although not so successful for one particular team). The first question I asked was “How happy are you at work?” I had them put a rating from 0 to 10, with 0 meaning they should have quit last week, and 10 meaning they couldn’t imaging being happier at work.

+

The answers were mostly 7s and 8s, with a 5 and a 9. The average came to 7.5, which is pretty good. The 5 concerns me a bit, especially that it’s 2 points lower than anyone else’s answer.

+

How effective do you think the teams is?

+

The next question I asked was “How effective do you think the teams is?”. Again, from 0 to 10, with 0 meaning they can’t accomplish anything, and 10 meaning you couldn’t imagine a more effective team.

+

When I ask both of these questions, the scores are always highly correlated. If your team isn’t doing good work, it’ll make you unhappy. And if you are unhappy, you’re less likely to do your best work. This team was no different; the 5 and 9 became a 6 and a 10, and most of the 7s became 8s, for an average of just under 8.

+

What makes you happy at work?

+

The next question I asked was “What makes you happy at work?”. The answers were mostly about the teamwork and teammates. This went quicker than I expected. At this point, I was worried the retro would only last a little more than 30 minutes.

+

What would make you happier at work?

+

The final question I asked was “What would make you happier at work?”. This was the real pay-off. We spent about half an hour just talking about the things that would make us happier, and what we could do to improve our happiness. We came up with 10 potential action items. I usually limit teams to trying 3 or 4 action items, but most of the items are quite small, so we’re going to try 6 of them. One is just observing another team’s standup meetings, to see how they’re using their time effectively.

+

Everyone went away feeling that this was a really good retro. It felt good to focus on happiness. Happiness is something I’ve been talking a lot about on the This Agile Life podcast, and it felt good to take some action on it. I’ve done “positive-only” retros before, but this one felt even better than that, by specifically targeting happiness and how we can achieve it.

+
+ + +
+ +
+
+ +

Impromptu Retrospective

+ + + +
+

I’m surprised that I haven’t gotten this story down in print before. It’s something I’ve mentioned many times — including a few times on the podcast. It’s a great story about the power of retrospectives, and it’s a great story about the power of a blameless post-mortem.

+

I don’t recall all the specifics at this point. It was about 5 years ago. I’d just noticed that Arun had made some sort of mistake. That’s fine, people make mistakes. The thing that was different about his mistake was that I had made the same mistake about a week prior. And Amos had made the same mistake about a week before that.

+

Noticing a pattern of mistakes, Amos and I called an impromptu retrospective. We gathered all the developers into a conference room. We explained the problem that we were running into. At first, Arun was defensive. That’s understandable; he thought we were there to come down on him, to lay blame. But we made it clear that we weren’t focusing on him. We admitted that we had also made the same mistake recently. We weren’t there to lay blame; we were there to figure out how our team could stop making the mistake. It took Arun a few minutes to get over the defensiveness.

+

With the defensiveness out of the way, we could focus on the issue at hand. We were able to figure out the root cause of us all making the mistake. (I don’t know if we played the “5 whys” game, but I’m sure we effectively did something similar.) And with that, we were able to change our process, so that nobody else would make the same mistake again.

+

There are 2 important points to this story. First, you don’t have to wait until a scheduled retrospective to hold a retrospective. This one was impromptu, and it’s the best one we ever had. We saw a problem, addressed it, and found a solution in less than an hour. Had we waited until the end of the week, we would have forgotten some of the details, and wouldn’t have been as effective at solving the problem. Second, when addressing problems, take your ego out of the equation. If you’re in a position of authority, take the blame — but never place blame. Focus on what’s important — solving the problem.

+

And don’t forget the Retrospective Prime Directive:

+

Regardless of what we discover, we understand and truly believe that everyone did the best job they could, given what they knew at the time, their skills and abilities, the resources available, and the situation at hand.

+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2015/11/02/impromptu-retrospective.html b/public/2015/11/02/impromptu-retrospective.html new file mode 100644 index 0000000..ca3737a --- /dev/null +++ b/public/2015/11/02/impromptu-retrospective.html @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + +Impromptu Retrospective – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Impromptu Retrospective

+ + + +
+

I’m surprised that I haven’t gotten this story down in print before. It’s something I’ve mentioned many times — including a few times on the podcast. It’s a great story about the power of retrospectives, and it’s a great story about the power of a blameless post-mortem.

+

I don’t recall all the specifics at this point. It was about 5 years ago. I’d just noticed that Arun had made some sort of mistake. That’s fine, people make mistakes. The thing that was different about his mistake was that I had made the same mistake about a week prior. And Amos had made the same mistake about a week before that.

+

Noticing a pattern of mistakes, Amos and I called an impromptu retrospective. We gathered all the developers into a conference room. We explained the problem that we were running into. At first, Arun was defensive. That’s understandable; he thought we were there to come down on him, to lay blame. But we made it clear that we weren’t focusing on him. We admitted that we had also made the same mistake recently. We weren’t there to lay blame; we were there to figure out how our team could stop making the mistake. It took Arun a few minutes to get over the defensiveness.

+

With the defensiveness out of the way, we could focus on the issue at hand. We were able to figure out the root cause of us all making the mistake. (I don’t know if we played the “5 whys” game, but I’m sure we effectively did something similar.) And with that, we were able to change our process, so that nobody else would make the same mistake again.

+

There are 2 important points to this story. First, you don’t have to wait until a scheduled retrospective to hold a retrospective. This one was impromptu, and it’s the best one we ever had. We saw a problem, addressed it, and found a solution in less than an hour. Had we waited until the end of the week, we would have forgotten some of the details, and wouldn’t have been as effective at solving the problem. Second, when addressing problems, take your ego out of the equation. If you’re in a position of authority, take the blame — but never place blame. Focus on what’s important — solving the problem.

+

And don’t forget the Retrospective Prime Directive:

+

Regardless of what we discover, we understand and truly believe that everyone did the best job they could, given what they knew at the time, their skills and abilities, the resources available, and the situation at hand.

+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/11/02/impromptu-retrospective/feed b/public/2015/11/02/impromptu-retrospective/feed new file mode 100644 index 0000000..6ed7d42 --- /dev/null +++ b/public/2015/11/02/impromptu-retrospective/feed @@ -0,0 +1,18 @@ + + + Comments on: Impromptu Retrospective + + http://blog.boochtek.com/2015/11/02/impromptu-retrospective + Web Development, Ruby on Rails, Open Source + Tue, 28 Jun 2016 22:02:28 +0000 + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/2015/11/09/happiness-retrospective.html b/public/2015/11/09/happiness-retrospective.html new file mode 100644 index 0000000..6c391cd --- /dev/null +++ b/public/2015/11/09/happiness-retrospective.html @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + +Happiness Retrospective – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Happiness Retrospective

+ + + +
+

I facilitated a retrospective today; it was one of the best retros I’ve ever been involved with. I figured out what activities I wanted to do earlier in the morning. They were really quite simple. I wanted to focus on happiness.

+

How happy are you at work?

+

I started with two questions that I’ve used with teams before, to some success (although not so successful for one particular team). The first question I asked was “How happy are you at work?” I had them put a rating from 0 to 10, with 0 meaning they should have quit last week, and 10 meaning they couldn’t imaging being happier at work.

+

The answers were mostly 7s and 8s, with a 5 and a 9. The average came to 7.5, which is pretty good. The 5 concerns me a bit, especially that it’s 2 points lower than anyone else’s answer.

+

How effective do you think the teams is?

+

The next question I asked was “How effective do you think the teams is?”. Again, from 0 to 10, with 0 meaning they can’t accomplish anything, and 10 meaning you couldn’t imagine a more effective team.

+

When I ask both of these questions, the scores are always highly correlated. If your team isn’t doing good work, it’ll make you unhappy. And if you are unhappy, you’re less likely to do your best work. This team was no different; the 5 and 9 became a 6 and a 10, and most of the 7s became 8s, for an average of just under 8.

+

What makes you happy at work?

+

The next question I asked was “What makes you happy at work?”. The answers were mostly about the teamwork and teammates. This went quicker than I expected. At this point, I was worried the retro would only last a little more than 30 minutes.

+

What would make you happier at work?

+

The final question I asked was “What would make you happier at work?”. This was the real pay-off. We spent about half an hour just talking about the things that would make us happier, and what we could do to improve our happiness. We came up with 10 potential action items. I usually limit teams to trying 3 or 4 action items, but most of the items are quite small, so we’re going to try 6 of them. One is just observing another team’s standup meetings, to see how they’re using their time effectively.

+

Everyone went away feeling that this was a really good retro. It felt good to focus on happiness. Happiness is something I’ve been talking a lot about on the This Agile Life podcast, and it felt good to take some action on it. I’ve done “positive-only” retros before, but this one felt even better than that, by specifically targeting happiness and how we can achieve it.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/11/09/happiness-retrospective/feed b/public/2015/11/09/happiness-retrospective/feed new file mode 100644 index 0000000..348334c --- /dev/null +++ b/public/2015/11/09/happiness-retrospective/feed @@ -0,0 +1,18 @@ + + + Comments on: Happiness Retrospective + + http://blog.boochtek.com/2015/11/09/happiness-retrospective + Web Development, Ruby on Rails, Open Source + Tue, 28 Jun 2016 22:02:28 +0000 + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/2015/11/24/show-and-tell.html b/public/2015/11/24/show-and-tell.html new file mode 100644 index 0000000..33c5587 --- /dev/null +++ b/public/2015/11/24/show-and-tell.html @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + +Show and Tell – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Show and Tell

+ + + +
+

I’m wrapping up my current consulting gig at Mercy in December, and starting a new gig at CenturyLink. I’m a software developer, but that’s only a part of what I do. What I really do is join a team and help them improve the way they work — both their processes and their technical skills.

+

I think this is a key differentiator for me as a consultant. Most consultants (and Agile coaches) come in and tell people what to do. I don’t like to just tell people what to do. I’d much prefer to work side-by-side with them, getting a better understanding of what their challenges are. Once I have a better understanding of the challenges, I’m able to better brainstorm some ideas to try. Then we can experiment to see what will work and what won’t.

+

Instead of telling people what to do, I show them how. Most people learn better from seeing than from hearing. They also learn better if you explain how and why, not just what. So showing them how to do something is more effective than telling them. By showing and doing, you can also set a good example. This is especially important when collaboration is a large part of what needs to be improved.

+

I’ve found that this style of consulting is more highly respected by everyone. I build trust with developers by working closely with them. Managers like to keep me around once they see how effective these methods can be, so the gigs I take on tend to last relatively long.

+

The biggest problem I have is explaining how this works. I don’t really know what to put on my résumé. Sometimes I call myself an Agile practitioner, and sometimes an Agile player/coach. But those aren’t terribly satisfying descriptions. I’m considering actually putting “I help teams improve the way they work — both their processes and their technical skills” on the résumé. But that seems awkward, and misses the show versus tell part. I’d be open to any suggestions.

+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/11/24/show-and-tell/feed b/public/2015/11/24/show-and-tell/feed new file mode 100644 index 0000000..fb0e4af --- /dev/null +++ b/public/2015/11/24/show-and-tell/feed @@ -0,0 +1,18 @@ + + + Comments on: Show and Tell + + http://blog.boochtek.com/2015/11/24/show-and-tell + Web Development, Ruby on Rails, Open Source + Tue, 28 Jun 2016 22:02:28 +0000 + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/2015/12.html b/public/2015/12.html new file mode 100644 index 0000000..c86a1d4 --- /dev/null +++ b/public/2015/12.html @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + +December 2015 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

2015 Year in Review

+ + + +
+

It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+
+ + +
+ +
+
+ +

Face Your Fears

+ + + +
+

I’ve always been someone who faces my fears.

+

I have a moderate case of arachnophobia. I don’t run away when I see a spider, but it creeps my out when one is crawling on me. When I was in college, I decided to buy a tarantula to attempt to get over my irrational fear of spiders. I thought I’d be able to get more comfortable with the tarantula over time, eventually to the point of letting it crawl on my arm. It didn’t work. Although I did find that my fear of tarantulas is rational — I got a terrible case of hives just from touching the urticating hairs that fell off into its water sponge.

+

Last week, I was on vacation in Mexico. One of the excursions we took involved jumping in the water a lot. I’m not a strong swimmer — mostly because I have a hard time closing my nose; I hold my nose when I jump in. We zip-lined into the water a lot. At one point, there was a cliff to dive into the water from. It was about 15 feet above the water. It didn’t look so far down before jumping. But it felt like a really long way down the first time I jumped from it. It was pretty scary for me. So I did it a second time. There wasn’t any peer pressure to jump a second time. I literally jumped a second time specifically because I was scared.

+

Fear is a weird thing. Fear is there to protect us. But it’s there to protect us from the dangers of the African savannah. Most of the things our fears protect us from don’t exist in our everyday modern lives. So maybe we should work to gain a better understanding of how our fears work, to figure out when to pay attention to them and when to ignore them.

+

Fortunately, our brain has a good mechanism to help us do this. Our brains basically have 2 main processing systems. The first one is for quick reactions. This one involves things that are nearly reflexes. Fear is in this system. The second system is our analytical reasoning system. This system takes longer to process, but is able to take on more information.

+

Whenever the situation allows us time for both systems to work, we need to listen to them both. We need to listen to our fears, because they’re there for a reason. But that reason might not pertain to our situation. So we need to realize that, and let the slow analytical system determine if we should ignore our fears.

+

If we don’t allow both systems to work, we’re not taking full advantage of our brains; we’re not taking full advantage of the situations that life is presenting to us.

+
+ + +
+ +
+
+ +

Encouragement

+ + + +
+

I’ve been on vacation the past week, in Cozumel, Mexico. One day, we went on an excursion called Xenotes. A cenote (say-NO-tay) is a sinkhole filled with fresh water. (The “X” is to give it a more Mayan-sounding trademarkable name.) We had a lot of fun swimming, kayaking, and zip-lining. A bilingual tour guide led our group, which consisted of people from across the US and South America, of various ages and physical abilities.

+

Our tour guide was a lot of fun. He made jokes, told us about the cenotes, and led us in the activities. But he also encouraged us. When someone was scared to do something, he was supportive. He told us that it was okay, and we could do it. But we also felt like it was OK to fail, if we really couldn’t. It really felt like his encouragement was literally creating courage.

+

What was really neat was that despite the language barrier, everyone else was also supportive and encouraging. Everyone cheered with encouragement before someone would attempt something difficult. And we’d cheer especially loud once someone accomplished something that was difficult for their abilities.

+

It was an awesome feeling to feel so supported. It made me feel like I was in a safe place, where I could try new things that were a little past my comfort zone. I was able to do the zip-line upside-down. I jumped off a 15-foot cliff into the water. I even jumped off the cliff a second time, even though I was a little scared.

+

Back at the resort, we played some volleyball in the pool. It was a similar situation, with players of varying ages and abilities. Again, we tried to help the weaker players feel comfortable so they could improve without feeling judged or self-conscious. It helped everyone have a good time. It made everything more fun to be in such a supportive environment, whether I was in a position as one of the more skilled (volleyball), or one of the less skilled (jumping or zip-lining into the water). We were all able to accomplish more, with less effort.

+

These experiences provide a good lesson that can be applied in a lot of places. Such a supportive environment would help any relationship, and any team. I’ve been on a couple really good software development teams, but I don’t think any of them have been as supportive as these two groups of strangers.

+

I’ve decided that this should be one of my goals as a team leader. I want to create an encouraging environment for the whole team. I want to make sure that everyone is comfortable enough that they feel like they can try things that are difficult, even if they might fail (as long as nobody gets hurt).

+

If a group of strangers can do this, so can your team. So can you and your significant others. We need to work every day to make sure that we’re supporting each other. It’s the best way to get everyone to achieve more.

+
+ + +
+ +
+
+ +

The Ultimate Optimization

+ + + +
+

I got my first computer in 1984. It was a Commodore 64. I had to do extra chores and save up my allowance to buy it. I was 13.

+

Back then, Sears and other retail stores had Commodore 64 computers out on display. Whenever I went to the store, I’d write a short BASIC program and leave it running on the display computers:

+
10 PRINT "CRAIG IS GREAT!"
+20 GOTO 10
+

Hey, I was a 13-year-old kid. Later, I got a little more sophisticated. I’d change the background color instead. I still remember the POKE address:

+
10 FOR I=0 TO 255
+20 POKE 53281, I
+30 NEXT I
+40 GOTO 10
+RUN
+

This was fast enough to change the background color every few scan lines, creating a flashing scrolling effect.

+

Later, I learned 6502 assembly language. I translated the BASIC program into assembler, and memorized the bytes to type in at the store. In assembly language, the background color would change several times per scan line. The effect was kind of psychedelic.

+

All that happened in the mid-1980s.

+

Fast-forward to about 2000 or so. I was telling the above story after a St. Louis LUG meeting. I explained how I had memorized the 10 or 12 bytes of machine code, and would leave the program running with its psychedelic effect.

+

After thinking about it for a bit, I thought that 10 or 12 bytes seemed too much. It actually bothered me — I couldn’t fall asleep when I got home. I got up and found my old 6502 manuals. I figured out how to write the code in 7 bytes. I installed the Vice C64 emulator on my Linux desktop, and tested my code. It worked as expected. (The emulator was already clock-cycle perfect by then.) Here’s the assembly code:

+
INX         ; $E8           ; 232
+STX $D021   ; $8E $21 $D0   ; 142 33 208   ; $D021 = 53281
+JMP $C000   ; $4C $00 $C0   ; 76 0 192     ; $C000 = 49152
+

Here’s the BASIC program to store that program in memory and run it:

+
10 FOR N=49152 TO 49152+6: READ Q : POKE N, Q : NEXT
+20 DATA 232, 142, 33, 208, 76, 0, 192
+30 SYS 49152
+RUN
+

The moral of the story is that you can optimize even a 10-byte program, 15 years after the last time it was used. So don’t tell me that your program can’t be improved, no matter how small it is.

+

PS. I rewrote the code above while writing this article in 2015, about 15 years after the last time I rewrote it. And I again downloaded Vice to test it, this time on Mac OS X.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2015/12/06/ultimate-optimization.html b/public/2015/12/06/ultimate-optimization.html new file mode 100644 index 0000000..3b8b486 --- /dev/null +++ b/public/2015/12/06/ultimate-optimization.html @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + +The Ultimate Optimization – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

The Ultimate Optimization

+ + + +
+

I got my first computer in 1984. It was a Commodore 64. I had to do extra chores and save up my allowance to buy it. I was 13.

+

Back then, Sears and other retail stores had Commodore 64 computers out on display. Whenever I went to the store, I’d write a short BASIC program and leave it running on the display computers:

+
10 PRINT "CRAIG IS GREAT!"
+20 GOTO 10
+

Hey, I was a 13-year-old kid. Later, I got a little more sophisticated. I’d change the background color instead. I still remember the POKE address:

+
10 FOR I=0 TO 255
+20 POKE 53281, I
+30 NEXT I
+40 GOTO 10
+RUN
+

This was fast enough to change the background color every few scan lines, creating a flashing scrolling effect.

+

Later, I learned 6502 assembly language. I translated the BASIC program into assembler, and memorized the bytes to type in at the store. In assembly language, the background color would change several times per scan line. The effect was kind of psychedelic.

+

All that happened in the mid-1980s.

+

Fast-forward to about 2000 or so. I was telling the above story after a St. Louis LUG meeting. I explained how I had memorized the 10 or 12 bytes of machine code, and would leave the program running with its psychedelic effect.

+

After thinking about it for a bit, I thought that 10 or 12 bytes seemed too much. It actually bothered me — I couldn’t fall asleep when I got home. I got up and found my old 6502 manuals. I figured out how to write the code in 7 bytes. I installed the Vice C64 emulator on my Linux desktop, and tested my code. It worked as expected. (The emulator was already clock-cycle perfect by then.) Here’s the assembly code:

+
INX         ; $E8           ; 232
+STX $D021   ; $8E $21 $D0   ; 142 33 208   ; $D021 = 53281
+JMP $C000   ; $4C $00 $C0   ; 76 0 192     ; $C000 = 49152
+

Here’s the BASIC program to store that program in memory and run it:

+
10 FOR N=49152 TO 49152+6: READ Q : POKE N, Q : NEXT
+20 DATA 232, 142, 33, 208, 76, 0, 192
+30 SYS 49152
+RUN
+

The moral of the story is that you can optimize even a 10-byte program, 15 years after the last time it was used. So don’t tell me that your program can’t be improved, no matter how small it is.

+

PS. I rewrote the code above while writing this article in 2015, about 15 years after the last time I rewrote it. And I again downloaded Vice to test it, this time on Mac OS X.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/12/06/ultimate-optimization/feed b/public/2015/12/06/ultimate-optimization/feed new file mode 100644 index 0000000..9423813 --- /dev/null +++ b/public/2015/12/06/ultimate-optimization/feed @@ -0,0 +1,18 @@ + + + Comments on: The Ultimate Optimization + + http://blog.boochtek.com/2015/12/06/ultimate-optimization + Web Development, Ruby on Rails, Open Source + Tue, 28 Jun 2016 22:02:28 +0000 + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/2015/12/15/encouragement.html b/public/2015/12/15/encouragement.html new file mode 100644 index 0000000..ce236a9 --- /dev/null +++ b/public/2015/12/15/encouragement.html @@ -0,0 +1,293 @@ + + + + + + + + + + + + + + + + +Encouragement – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Encouragement

+ + + +
+

I’ve been on vacation the past week, in Cozumel, Mexico. One day, we went on an excursion called Xenotes. A cenote (say-NO-tay) is a sinkhole filled with fresh water. (The “X” is to give it a more Mayan-sounding trademarkable name.) We had a lot of fun swimming, kayaking, and zip-lining. A bilingual tour guide led our group, which consisted of people from across the US and South America, of various ages and physical abilities.

+

Our tour guide was a lot of fun. He made jokes, told us about the cenotes, and led us in the activities. But he also encouraged us. When someone was scared to do something, he was supportive. He told us that it was okay, and we could do it. But we also felt like it was OK to fail, if we really couldn’t. It really felt like his encouragement was literally creating courage.

+

What was really neat was that despite the language barrier, everyone else was also supportive and encouraging. Everyone cheered with encouragement before someone would attempt something difficult. And we’d cheer especially loud once someone accomplished something that was difficult for their abilities.

+

It was an awesome feeling to feel so supported. It made me feel like I was in a safe place, where I could try new things that were a little past my comfort zone. I was able to do the zip-line upside-down. I jumped off a 15-foot cliff into the water. I even jumped off the cliff a second time, even though I was a little scared.

+

Back at the resort, we played some volleyball in the pool. It was a similar situation, with players of varying ages and abilities. Again, we tried to help the weaker players feel comfortable so they could improve without feeling judged or self-conscious. It helped everyone have a good time. It made everything more fun to be in such a supportive environment, whether I was in a position as one of the more skilled (volleyball), or one of the less skilled (jumping or zip-lining into the water). We were all able to accomplish more, with less effort.

+

These experiences provide a good lesson that can be applied in a lot of places. Such a supportive environment would help any relationship, and any team. I’ve been on a couple really good software development teams, but I don’t think any of them have been as supportive as these two groups of strangers.

+

I’ve decided that this should be one of my goals as a team leader. I want to create an encouraging environment for the whole team. I want to make sure that everyone is comfortable enough that they feel like they can try things that are difficult, even if they might fail (as long as nobody gets hurt).

+

If a group of strangers can do this, so can your team. So can you and your significant others. We need to work every day to make sure that we’re supporting each other. It’s the best way to get everyone to achieve more.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/12/15/encouragement/feed b/public/2015/12/15/encouragement/feed new file mode 100644 index 0000000..fc41b32 --- /dev/null +++ b/public/2015/12/15/encouragement/feed @@ -0,0 +1,18 @@ + + + Comments on: Encouragement + + http://blog.boochtek.com/2015/12/15/encouragement + Web Development, Ruby on Rails, Open Source + Tue, 28 Jun 2016 22:02:28 +0000 + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/2015/12/21/face-your-fears.html b/public/2015/12/21/face-your-fears.html new file mode 100644 index 0000000..ac2a654 --- /dev/null +++ b/public/2015/12/21/face-your-fears.html @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + +Face Your Fears – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Face Your Fears

+ + + +
+

I’ve always been someone who faces my fears.

+

I have a moderate case of arachnophobia. I don’t run away when I see a spider, but it creeps my out when one is crawling on me. When I was in college, I decided to buy a tarantula to attempt to get over my irrational fear of spiders. I thought I’d be able to get more comfortable with the tarantula over time, eventually to the point of letting it crawl on my arm. It didn’t work. Although I did find that my fear of tarantulas is rational — I got a terrible case of hives just from touching the urticating hairs that fell off into its water sponge.

+

Last week, I was on vacation in Mexico. One of the excursions we took involved jumping in the water a lot. I’m not a strong swimmer — mostly because I have a hard time closing my nose; I hold my nose when I jump in. We zip-lined into the water a lot. At one point, there was a cliff to dive into the water from. It was about 15 feet above the water. It didn’t look so far down before jumping. But it felt like a really long way down the first time I jumped from it. It was pretty scary for me. So I did it a second time. There wasn’t any peer pressure to jump a second time. I literally jumped a second time specifically because I was scared.

+

Fear is a weird thing. Fear is there to protect us. But it’s there to protect us from the dangers of the African savannah. Most of the things our fears protect us from don’t exist in our everyday modern lives. So maybe we should work to gain a better understanding of how our fears work, to figure out when to pay attention to them and when to ignore them.

+

Fortunately, our brain has a good mechanism to help us do this. Our brains basically have 2 main processing systems. The first one is for quick reactions. This one involves things that are nearly reflexes. Fear is in this system. The second system is our analytical reasoning system. This system takes longer to process, but is able to take on more information.

+

Whenever the situation allows us time for both systems to work, we need to listen to them both. We need to listen to our fears, because they’re there for a reason. But that reason might not pertain to our situation. So we need to realize that, and let the slow analytical system determine if we should ignore our fears.

+

If we don’t allow both systems to work, we’re not taking full advantage of our brains; we’re not taking full advantage of the situations that life is presenting to us.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/12/21/face-your-fears/feed b/public/2015/12/21/face-your-fears/feed new file mode 100644 index 0000000..e01b888 --- /dev/null +++ b/public/2015/12/21/face-your-fears/feed @@ -0,0 +1,18 @@ + + + Comments on: Face Your Fears + + http://blog.boochtek.com/2015/12/21/face-your-fears + Web Development, Ruby on Rails, Open Source + Tue, 28 Jun 2016 22:02:28 +0000 + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/2015/12/28/year-in-review-2015.html b/public/2015/12/28/year-in-review-2015.html new file mode 100644 index 0000000..1dd4502 --- /dev/null +++ b/public/2015/12/28/year-in-review-2015.html @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + +2015 Year in Review – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

2015 Year in Review

+ + + +
+

It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2015/12/28/year-in-review-2015/feed b/public/2015/12/28/year-in-review-2015/feed new file mode 100644 index 0000000..5ca3503 --- /dev/null +++ b/public/2015/12/28/year-in-review-2015/feed @@ -0,0 +1,18 @@ + + + Comments on: 2015 Year in Review + + http://blog.boochtek.com/2015/12/28/year-in-review-2015 + Web Development, Ruby on Rails, Open Source + Tue, 28 Jun 2016 22:02:28 +0000 + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/2016/01.html b/public/2016/01.html new file mode 100644 index 0000000..2141ea8 --- /dev/null +++ b/public/2016/01.html @@ -0,0 +1,249 @@ + + + + + + + + + + + + + + + +January 2016 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Resolutions for 2016

+ + + +
+

I’ve written up a list of career-related resolutions the past few years. I’ve been pretty successful meeting those goals, so I’m going to continue the tradition.

+

Coding

+

I want to increase the Open Source code I write this year. That’s basically everything I write that’s not directly related to work.

+

I took over maintenance of Virtus late last year, but haven’t done a good job at finding the time for that. That’s probably more responding to issues on GitHub and merging pull requests. But eventually, I’ll likely add some features and do some refactoring.

+

My goal is to work on 6 apps and libraries this year. That’s a pretty aggressive goal, but I want to focus on both learning new things and building some simple but useful things. Think MVP — focus on getting a lot of bang for not too much effort.

+

I want to work on a new Rails app, to familiarize myself with Rails 5. I want to get some experience with some more modern gems. I also want to see if I can use some new techniques with a Rails app, especially things that might help us get closer to a hexagonal architecture. I’m also hoping to work with some different web frameworks — maybe Lotus, Rodakase, Trailblazer, or Phoenix. Or maybe one of the Crystal web frameworks.

+

I also want to work on a couple Elm apps. I started the STL Elm group as an excuse to learn the language. I’d like to work on a Twitter client, that might eventually turn into a more general reading app. Beyond Twitter, it would keep track of things I want to read. The other Elm app I’d like to write is a game, based on the old Space Taxi game I loved to play on the Commodore 64.

+

I wrote a micro-ORM last year called Ruby Preserves. I’ve started converting that from working with raw SQL to sitting atop the awesome Sequel gem. The simplicity is still there so far, and basing it on Sequel has so far gone easier than I expected. But I haven’t figured out how to do relationships (JOINs) yet; that will be the real test.

+

Conferences

+

I’m pretty happy with my 2015 conference experience. I want to keep my conference level about the same. I don’t really want to commit to more talks than I gave last year. Conference presentations take a lot of time and energy to write and practice.

+

I’m planning to go to RailsConf, RubyConf, and Strange Loop. I’m hoping to give a talk at each of those, if I can. But I’ll probably attend all of them even if I’m not speaking. I’m also submitting a talk to Agile 2016. I’m unlikely to go to any other conferences, and even more unlikely to give more than 4 conference talks.

+

Writing

+

I really want to finish my Effective Agile book this year. I’m going to try to pull Amos in to pair with me on it occasionally, to move it forward. If I’m able to get it done, I’d also like to maybe write a book on Elm.

+

I actually want to do less blogging this year. Well, sort of. I want to put coding and book writing ahead of blogging. If that means less blogging, I’m okay with that. So my goal for blogging is more like every other week. And when I do write a blog article, I’d rather it be related to some code I’m working on, something that I could use in my book, or some more in-depth thoughts about programming language design.

+

Job Hunting

+

The contract I recently started at CenturyLink Cloud is open-ended, so I’ll likely be there for the entire year. I’m enjoying it so far; it’s a challenge, but I think I can make a significant impact. However, I’m really interested in moving to San Francisco in 2017. So I’m going to work on preparing for that in several ways.

+

I’m going to resurrect my LinkedIn account. I’ve neglected it for a few years now. I’ll make sure everything is up to date, and make some connections I’ve been putting off. I also want to reclaim my Stack Exchange and Hacker News accounts.

+

I’m going to pay a an expert to help me revamp my résumé. I think it’s pretty decent, but it could use some freshening up. I want it to stand out. I want to do a better job of explaining what I really do — which is to join a team to help them improve their processes and their technical skills.

+

I’ll also need to clean up all my web sites, so they look nice when people come to take a look at them. This includes my personal site, consulting business site, wiki, and blogs. I should also upgrade my server to the latest version of Debian, and use Ansible and some other tools to provision it using the principles of Infrastructure as Code.

+
+ + +
+ +
+
+ +

Team Values

+ + + +
+

I held a retrospective with my new team last week. The team includes 2 senior developers, 2 junior developers, a product owner, and a product analyst. I’ve joined the team as an engineering manager, which I think of more as a team lead with an elevated title. Being new to this group, I wanted a way to understand their values. What motivates them? What common values do we share that we can leverage to move forward in the same direction?

+

I started out with a pretty simple question: “What do you value (in regards to what we’re building); what are you willing to fight for?” I asked them each to write down several values and then put them on the board before looking at everyone else’s answers. I also asked each person to rank their values in order of importance.

+

It turned out that my question was a little vague. Some people thought about the question in terms of the end result, and some in terms of the process of creating the software. In some ways that was a bit of a problem, because the different interpretations led people in different directions. But in other ways, not pushing them in any particular direction got more varied answers, exposing how they think about the project and the product.

+

My list (in order) was Effectiveness (doing the right thing), Quality (doing things right the first time), Happiness, Purpose, and Teamwork. In retrospect, I should have put Happiness first. If I’m not happy at work, I don’t really want to be there, and need to move on. (Sometimes I can trade some happiness at work for more happiness at home, but I’m definitely a person that needs to be happy at work.) The other values serve to improve my happiness, but the happiness is more important.

+

My own issue ranking my values turned out to be a problem with the ranking in general. Should they be ranked in importance of the necessity of the value as an outcome, or in importance of the necessity to focus on the value? I think the former is what I was looking for, but even I wasn’t clear on that when I began the exercise.

+

After everyone put their values up on the board, we read them off. Then I asked the team to choose several values that we share as a team. We pulled them off the individual members’ lists, and put them in the team list. Then I asked each person to come up and rank those values, then explain why they had ordered them that way, especially when they ordered them significantly different than the last person. I was hoping to come to some convergence of the rank over time, so we could document our values in rank order. That didn’t happen. But the discussion was illuminating to me and to everyone on the team.

+

I think the most interesting part about the lack of convergence was the difference between the developers and the product guys. The product guys definitely viewed the values more in terms of outcomes than the process. That makes sense — they’re not as intimately involved in the process of building the product.

+

We were able to converge on the top priority though: Will the user buy the product? This was a combination of a couple different values that we merged together. This included the end user experience as well as making sure the team would continue to have a reason for existing. The rest of the values we left unordered: Teamwork (cohesiveness), Data driven decisions, Team ownership, Simplicity, Effectiveness, Maintainability / Supportability, Quality, Automation, and Performance. I think that’s a pretty decent list.

+

As a couple teammates pointed out, those values are probably in part a reflection of this current point in time. If I asked the same question some other time, under different conditions and team dynamics, the answers would probably change a bit. And we’d probably come up with other answers if asked again, just due to randomness of the way we think about these things.

+

But I don’t think I’d do this activity a second time with the team. It was really about understanding our motivations — both our own, and those of our teammates. I found it effective in that way, and also in helping the team to think about our culture and how we can work to shape it to help us all push in the same direction.

+

There are a few caveats. When I asked for feedback on the exercise, one teammate pointed out that it wouldn’t work if people weren’t honest about their values, and they answered with what they thought management or their teammates wanted to hear. I don’t think that was an issue with this group, but it’s something to keep in mind.

+

The other major thing I’d do is to make it clear up front that I’m not looking for any action items from this activity; it’s more about understanding each other and ourselves. And next time, I’ll work to clarify how to rank the values.

+

I would recommend this activity for a new team, or when the makeup of the team is changing in some significant way. I wish there was a way to help a team converge on the ranking of their values, but I suppose I should be happy that agreeing on the set of important values went pretty quickly. And the diversity of ideas and opinions is probably a blessing that I should be embracing — the more ideas we have, the wider the variety of solutions we can imagine.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2016/01/04/team-values.html b/public/2016/01/04/team-values.html new file mode 100644 index 0000000..2c4573c --- /dev/null +++ b/public/2016/01/04/team-values.html @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + +Team Values – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Team Values

+ + + +
+

I held a retrospective with my new team last week. The team includes 2 senior developers, 2 junior developers, a product owner, and a product analyst. I’ve joined the team as an engineering manager, which I think of more as a team lead with an elevated title. Being new to this group, I wanted a way to understand their values. What motivates them? What common values do we share that we can leverage to move forward in the same direction?

+

I started out with a pretty simple question: “What do you value (in regards to what we’re building); what are you willing to fight for?” I asked them each to write down several values and then put them on the board before looking at everyone else’s answers. I also asked each person to rank their values in order of importance.

+

It turned out that my question was a little vague. Some people thought about the question in terms of the end result, and some in terms of the process of creating the software. In some ways that was a bit of a problem, because the different interpretations led people in different directions. But in other ways, not pushing them in any particular direction got more varied answers, exposing how they think about the project and the product.

+

My list (in order) was Effectiveness (doing the right thing), Quality (doing things right the first time), Happiness, Purpose, and Teamwork. In retrospect, I should have put Happiness first. If I’m not happy at work, I don’t really want to be there, and need to move on. (Sometimes I can trade some happiness at work for more happiness at home, but I’m definitely a person that needs to be happy at work.) The other values serve to improve my happiness, but the happiness is more important.

+

My own issue ranking my values turned out to be a problem with the ranking in general. Should they be ranked in importance of the necessity of the value as an outcome, or in importance of the necessity to focus on the value? I think the former is what I was looking for, but even I wasn’t clear on that when I began the exercise.

+

After everyone put their values up on the board, we read them off. Then I asked the team to choose several values that we share as a team. We pulled them off the individual members’ lists, and put them in the team list. Then I asked each person to come up and rank those values, then explain why they had ordered them that way, especially when they ordered them significantly different than the last person. I was hoping to come to some convergence of the rank over time, so we could document our values in rank order. That didn’t happen. But the discussion was illuminating to me and to everyone on the team.

+

I think the most interesting part about the lack of convergence was the difference between the developers and the product guys. The product guys definitely viewed the values more in terms of outcomes than the process. That makes sense — they’re not as intimately involved in the process of building the product.

+

We were able to converge on the top priority though: Will the user buy the product? This was a combination of a couple different values that we merged together. This included the end user experience as well as making sure the team would continue to have a reason for existing. The rest of the values we left unordered: Teamwork (cohesiveness), Data driven decisions, Team ownership, Simplicity, Effectiveness, Maintainability / Supportability, Quality, Automation, and Performance. I think that’s a pretty decent list.

+

As a couple teammates pointed out, those values are probably in part a reflection of this current point in time. If I asked the same question some other time, under different conditions and team dynamics, the answers would probably change a bit. And we’d probably come up with other answers if asked again, just due to randomness of the way we think about these things.

+

But I don’t think I’d do this activity a second time with the team. It was really about understanding our motivations — both our own, and those of our teammates. I found it effective in that way, and also in helping the team to think about our culture and how we can work to shape it to help us all push in the same direction.

+

There are a few caveats. When I asked for feedback on the exercise, one teammate pointed out that it wouldn’t work if people weren’t honest about their values, and they answered with what they thought management or their teammates wanted to hear. I don’t think that was an issue with this group, but it’s something to keep in mind.

+

The other major thing I’d do is to make it clear up front that I’m not looking for any action items from this activity; it’s more about understanding each other and ourselves. And next time, I’ll work to clarify how to rank the values.

+

I would recommend this activity for a new team, or when the makeup of the team is changing in some significant way. I wish there was a way to help a team converge on the ranking of their values, but I suppose I should be happy that agreeing on the set of important values went pretty quickly. And the diversity of ideas and opinions is probably a blessing that I should be embracing — the more ideas we have, the wider the variety of solutions we can imagine.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2016/01/04/team-values/feed b/public/2016/01/04/team-values/feed new file mode 100644 index 0000000..a533394 --- /dev/null +++ b/public/2016/01/04/team-values/feed @@ -0,0 +1,18 @@ + + + Comments on: Team Values + + http://blog.boochtek.com/2016/01/04/team-values + Web Development, Ruby on Rails, Open Source + Tue, 28 Jun 2016 22:02:28 +0000 + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/2016/01/19/resolutions-2016.html b/public/2016/01/19/resolutions-2016.html new file mode 100644 index 0000000..842b4bd --- /dev/null +++ b/public/2016/01/19/resolutions-2016.html @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + +Resolutions for 2016 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Resolutions for 2016

+ + + +
+

I’ve written up a list of career-related resolutions the past few years. I’ve been pretty successful meeting those goals, so I’m going to continue the tradition.

+

Coding

+

I want to increase the Open Source code I write this year. That’s basically everything I write that’s not directly related to work.

+

I took over maintenance of Virtus late last year, but haven’t done a good job at finding the time for that. That’s probably more responding to issues on GitHub and merging pull requests. But eventually, I’ll likely add some features and do some refactoring.

+

My goal is to work on 6 apps and libraries this year. That’s a pretty aggressive goal, but I want to focus on both learning new things and building some simple but useful things. Think MVP — focus on getting a lot of bang for not too much effort.

+

I want to work on a new Rails app, to familiarize myself with Rails 5. I want to get some experience with some more modern gems. I also want to see if I can use some new techniques with a Rails app, especially things that might help us get closer to a hexagonal architecture. I’m also hoping to work with some different web frameworks — maybe Lotus, Rodakase, Trailblazer, or Phoenix. Or maybe one of the Crystal web frameworks.

+

I also want to work on a couple Elm apps. I started the STL Elm group as an excuse to learn the language. I’d like to work on a Twitter client, that might eventually turn into a more general reading app. Beyond Twitter, it would keep track of things I want to read. The other Elm app I’d like to write is a game, based on the old Space Taxi game I loved to play on the Commodore 64.

+

I wrote a micro-ORM last year called Ruby Preserves. I’ve started converting that from working with raw SQL to sitting atop the awesome Sequel gem. The simplicity is still there so far, and basing it on Sequel has so far gone easier than I expected. But I haven’t figured out how to do relationships (JOINs) yet; that will be the real test.

+

Conferences

+

I’m pretty happy with my 2015 conference experience. I want to keep my conference level about the same. I don’t really want to commit to more talks than I gave last year. Conference presentations take a lot of time and energy to write and practice.

+

I’m planning to go to RailsConf, RubyConf, and Strange Loop. I’m hoping to give a talk at each of those, if I can. But I’ll probably attend all of them even if I’m not speaking. I’m also submitting a talk to Agile 2016. I’m unlikely to go to any other conferences, and even more unlikely to give more than 4 conference talks.

+

Writing

+

I really want to finish my Effective Agile book this year. I’m going to try to pull Amos in to pair with me on it occasionally, to move it forward. If I’m able to get it done, I’d also like to maybe write a book on Elm.

+

I actually want to do less blogging this year. Well, sort of. I want to put coding and book writing ahead of blogging. If that means less blogging, I’m okay with that. So my goal for blogging is more like every other week. And when I do write a blog article, I’d rather it be related to some code I’m working on, something that I could use in my book, or some more in-depth thoughts about programming language design.

+

Job Hunting

+

The contract I recently started at CenturyLink Cloud is open-ended, so I’ll likely be there for the entire year. I’m enjoying it so far; it’s a challenge, but I think I can make a significant impact. However, I’m really interested in moving to San Francisco in 2017. So I’m going to work on preparing for that in several ways.

+

I’m going to resurrect my LinkedIn account. I’ve neglected it for a few years now. I’ll make sure everything is up to date, and make some connections I’ve been putting off. I also want to reclaim my Stack Exchange and Hacker News accounts.

+

I’m going to pay a an expert to help me revamp my résumé. I think it’s pretty decent, but it could use some freshening up. I want it to stand out. I want to do a better job of explaining what I really do — which is to join a team to help them improve their processes and their technical skills.

+

I’ll also need to clean up all my web sites, so they look nice when people come to take a look at them. This includes my personal site, consulting business site, wiki, and blogs. I should also upgrade my server to the latest version of Debian, and use Ansible and some other tools to provision it using the principles of Infrastructure as Code.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2016/01/19/resolutions-2016/feed b/public/2016/01/19/resolutions-2016/feed new file mode 100644 index 0000000..1e123f7 --- /dev/null +++ b/public/2016/01/19/resolutions-2016/feed @@ -0,0 +1,18 @@ + + + Comments on: Resolutions for 2016 + + http://blog.boochtek.com/2016/01/19/resolutions-2016 + Web Development, Ruby on Rails, Open Source + Tue, 28 Jun 2016 22:02:28 +0000 + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/2016/02.html b/public/2016/02.html new file mode 100644 index 0000000..acfdb0a --- /dev/null +++ b/public/2016/02.html @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + +February 2016 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

My First Open Space

+ + + +
+

I recently attended an Open Space hosted at work. I’d never been to an Open Space, and didn’t know what to expect. We’d been told that this was a workshop to help Engineering Managers (my role), Product Owners, and Product Analysts find better ways of working together. But due to the way an Open Space works, it evolved into something completely different — and better.

+

We’d brought in Diana Larsen to facilitate the Open Space. Diana is a stalwart of the Agile community, focusing on how people and teams interact. She literally (co-)wrote the book on Agile retrospectives. Diana was also kind enough to be our guest at an Agile LINC meetup later in the evening.

+

The morning started off with all the participants sitting in chairs arranged in a circle. Diana rang a chime, a nice soothing tone that literally set the tone for the day. (Most people didn’t seem to like it, but I thought it had its purpose.) It also acted as a call to gather in the meeting space. She then walked around the inside of the circle as she explained what was going to happen.

+

The idea behind Open Spaces came with the realization that at a conference, the “hallway track” (ad hoc discussions in the hallways) was often more valuable than the scheduled talks. So they figured out a way to capture that experience. There are only a few rules:

+
    +
  • Whoever shows up is the right group
  • +
  • Whatever happens is the only thing that could have
  • +
  • Be prepared to be surprised
  • +
  • Whenever it starts is the right time
  • +
  • When it’s over, it’s over
  • +
  • The Law of 2 Feet: If you’re not learning or contributing, go somewhere else
  • +
+

Once Diana set the scene and explained the rules, people came up and presented ideas for topics. If you proposed a topic, you chose a time and location on the topic board. At that time, you’d facilitate a discussion on that topic. The format was really conducive to discussions. There was no preparation, so discussions were from the heart — lots of people felt that they could contribute.

+

Being new to the company (only 2 months), I got a lot out of the workshop, beyond the content itself. I got to get to know several more people. I’d even say I made a few friends. Having open discussions, we found that many of the teams were having the same issues. This made it easy for me to talk to other people.

+

All-in-all, I found the Open Space to be extremely conducive to discussions aimed at identifying issues, brainstorming solutions, and planning action items. I felt like we made great strides in addressing some of the biggest problems our organization is currently facing.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2016/02/10/first-open-space.html b/public/2016/02/10/first-open-space.html new file mode 100644 index 0000000..ac21afd --- /dev/null +++ b/public/2016/02/10/first-open-space.html @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + +My First Open Space – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

My First Open Space

+ + + +
+

I recently attended an Open Space hosted at work. I’d never been to an Open Space, and didn’t know what to expect. We’d been told that this was a workshop to help Engineering Managers (my role), Product Owners, and Product Analysts find better ways of working together. But due to the way an Open Space works, it evolved into something completely different — and better.

+

We’d brought in Diana Larsen to facilitate the Open Space. Diana is a stalwart of the Agile community, focusing on how people and teams interact. She literally (co-)wrote the book on Agile retrospectives. Diana was also kind enough to be our guest at an Agile LINC meetup later in the evening.

+

The morning started off with all the participants sitting in chairs arranged in a circle. Diana rang a chime, a nice soothing tone that literally set the tone for the day. (Most people didn’t seem to like it, but I thought it had its purpose.) It also acted as a call to gather in the meeting space. She then walked around the inside of the circle as she explained what was going to happen.

+

The idea behind Open Spaces came with the realization that at a conference, the “hallway track” (ad hoc discussions in the hallways) was often more valuable than the scheduled talks. So they figured out a way to capture that experience. There are only a few rules:

+
    +
  • Whoever shows up is the right group
  • +
  • Whatever happens is the only thing that could have
  • +
  • Be prepared to be surprised
  • +
  • Whenever it starts is the right time
  • +
  • When it’s over, it’s over
  • +
  • The Law of 2 Feet: If you’re not learning or contributing, go somewhere else
  • +
+

Once Diana set the scene and explained the rules, people came up and presented ideas for topics. If you proposed a topic, you chose a time and location on the topic board. At that time, you’d facilitate a discussion on that topic. The format was really conducive to discussions. There was no preparation, so discussions were from the heart — lots of people felt that they could contribute.

+

Being new to the company (only 2 months), I got a lot out of the workshop, beyond the content itself. I got to get to know several more people. I’d even say I made a few friends. Having open discussions, we found that many of the teams were having the same issues. This made it easy for me to talk to other people.

+

All-in-all, I found the Open Space to be extremely conducive to discussions aimed at identifying issues, brainstorming solutions, and planning action items. I felt like we made great strides in addressing some of the biggest problems our organization is currently facing.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2016/02/10/first-open-space/feed b/public/2016/02/10/first-open-space/feed new file mode 100644 index 0000000..26c8946 --- /dev/null +++ b/public/2016/02/10/first-open-space/feed @@ -0,0 +1,18 @@ + + + Comments on: My First Open Space + + http://blog.boochtek.com/2016/02/10/first-open-space + Web Development, Ruby on Rails, Open Source + Tue, 28 Jun 2016 22:02:28 +0000 + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/2016/07.html b/public/2016/07.html new file mode 100644 index 0000000..266b532 --- /dev/null +++ b/public/2016/07.html @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + +July 2016 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

You Don’t Have to Be Right

+ + + +
+

Not too long ago, I was asked during a job interview “how do you convince your teammates that you’re right?” I answered with probably the most surprising answer: “I don’t.”

+

It’s taken me years to realize that being right isn’t terribly important. Especially when there’s more than one right answer — which there usually is. It’s more important to work as a team. It’s more important to be respected and to have respect for others.

+

A few weeks after that interview, I was pair programming with the person who had asked me the question. We’re both senior developers, so we both have a lot of experience and opinions based on that experience. We were writing a shell script, and we both had an idea in mind about how to write the code. I let him proceed with his idea. It was a pretty good idea; probably better than mine. But we paired very effectively. I’d let him finish some code, and I’d find a way to make it better. Then he’d find a way to make my code better. What we ended up with was so much better than my original idea, and his original idea as well. I commented on this, and he agreed. We left that pairing session feeling the high of having accomplished something rare — two very experience programmers coming up with better code than we could have imagined going in.

+

I like to think of writing code as a path towards a destination. The destination is good code that does what it’s supposed to, is readable (intention revealing), is concise, and is well-factored so as to be easy to change in the future. But there are many paths to that destination. Especially when pair programming, the code gets refined as ideas are shared, getting you closer and closer to that destination. Sometimes the different paths end up with the same code; sometimes they end up with different code. But if the code does its job well (according to those criteria), it doesn’t really matter what code you ended up with.

+

Thinking back to the original question, I think the answer is a little more nuanced. (I’m pretty sure I followed up with an explanation, but I don’t recall the details.) It truly doesn’t matter in a lot of cases. If there are different paths that lead to the same destination, and they’re all roughly equal, then my rule works. It can also work even if the first path you try (a “wrong” path) doesn’t work out, as long as it’s easy enough to try a different path. I find that the majority of day-to-day coding fits these conditions.

+

But there are some places where it’s costly to get things wrong the first time. In these cases, it’s worth spending some time thinking about and discussing the alternatives before getting started. Software architecture is the big one that comes to mind. In general, weighing the pros and cons of each option and applying previous experience works best.

+

So next time you think about trying to convince someone you’re right, fight the urge. Instead, let them show you what they’re thinking. They just might surprise you. And more importantly, you might surprise yourself.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/2016/07/06/you-dont-have-to-be-right.html b/public/2016/07/06/you-dont-have-to-be-right.html new file mode 100644 index 0000000..f14be2e --- /dev/null +++ b/public/2016/07/06/you-dont-have-to-be-right.html @@ -0,0 +1,331 @@ + + + + + + + + + + + + + + + + +You Don’t Have to Be Right – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

You Don’t Have to Be Right

+ + + +
+

Not too long ago, I was asked during a job interview “how do you convince your teammates that you’re right?” I answered with probably the most surprising answer: “I don’t.”

+

It’s taken me years to realize that being right isn’t terribly important. Especially when there’s more than one right answer — which there usually is. It’s more important to work as a team. It’s more important to be respected and to have respect for others.

+

A few weeks after that interview, I was pair programming with the person who had asked me the question. We’re both senior developers, so we both have a lot of experience and opinions based on that experience. We were writing a shell script, and we both had an idea in mind about how to write the code. I let him proceed with his idea. It was a pretty good idea; probably better than mine. But we paired very effectively. I’d let him finish some code, and I’d find a way to make it better. Then he’d find a way to make my code better. What we ended up with was so much better than my original idea, and his original idea as well. I commented on this, and he agreed. We left that pairing session feeling the high of having accomplished something rare — two very experience programmers coming up with better code than we could have imagined going in.

+

I like to think of writing code as a path towards a destination. The destination is good code that does what it’s supposed to, is readable (intention revealing), is concise, and is well-factored so as to be easy to change in the future. But there are many paths to that destination. Especially when pair programming, the code gets refined as ideas are shared, getting you closer and closer to that destination. Sometimes the different paths end up with the same code; sometimes they end up with different code. But if the code does its job well (according to those criteria), it doesn’t really matter what code you ended up with.

+

Thinking back to the original question, I think the answer is a little more nuanced. (I’m pretty sure I followed up with an explanation, but I don’t recall the details.) It truly doesn’t matter in a lot of cases. If there are different paths that lead to the same destination, and they’re all roughly equal, then my rule works. It can also work even if the first path you try (a “wrong” path) doesn’t work out, as long as it’s easy enough to try a different path. I find that the majority of day-to-day coding fits these conditions.

+

But there are some places where it’s costly to get things wrong the first time. In these cases, it’s worth spending some time thinking about and discussing the alternatives before getting started. Software architecture is the big one that comes to mind. In general, weighing the pros and cons of each option and applying previous experience works best.

+

So next time you think about trying to convince someone you’re right, fight the urge. Instead, let them show you what they’re thinking. They just might surprise you. And more importantly, you might surprise yourself.

+
+ + +
+ +
+ +

+ 3 thoughts on “You Don’t Have to Be Right”

+ + +
    +
  1. + +
  2. +
  3. +
    + + +
    +

    Hello, I have read your ideas about the Brilliant Programming Language.

    +

    I was wondering if you had made any progress with it? It seems like a language I would enjoy working with.

    +

    Best of luck.

    +
    + +
    +
  4. +
  5. + +
  6. +
+ + + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/2016/07/06/you-dont-have-to-be-right/feed b/public/2016/07/06/you-dont-have-to-be-right/feed new file mode 100644 index 0000000..ec6e75e --- /dev/null +++ b/public/2016/07/06/you-dont-have-to-be-right/feed @@ -0,0 +1,18 @@ + + + Comments on: You Don’t Have to Be Right + + http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right + Web Development, Ruby on Rails, Open Source + Tue, 28 Jun 2016 22:02:28 +0000 + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/README.md b/public/README.md new file mode 100644 index 0000000..ce2ce52 --- /dev/null +++ b/public/README.md @@ -0,0 +1,44 @@ +# How to download a site from the Archive.org Wayback Machine + +~~~ shell +WEB_SITE='https://blog.boochtek.com/' +TIME='20230101000000' + +git clone git@github.com:hartator/wayback-machine-downloader.git +cd wayback-machine-downloader +cd .. + +brew install waybackpy +waybackpy --url $WEB_SITE --from 2023-01-01 --known_urls > urls.txt + +# Add Wayback Machine URL prefix to the URLs. +awk -v prefix="https://web.archive.org/web/$TIME/" '{print prefix $0}' urls.txt > wayback-urls.txt + +# Figured out --adjust-extension via https://lists.gnu.org/archive/html/bug-wget/2015-06/msg00021.html. +# NOTE: We might find it useful to add `--directory-prefix=$TARGET_DIR`. +wget --input-file=wayback-urls.txt --continue --wait=4 --retry-connrefused \ + --force-directories --no-host-directories --cut-dirs=5 --adjust-extension + # --directory-prefix=. + +# Convert URLs in all files to remove the Wayback Machine URL prefix. +sed -i '' -e "s|https://web.archive.org/web/.*/||g" $(find . -type f) + +rm -rf 201* index.html + +# Convert all HTML files into MarkDown, recursively. +if ! brew list | grep pandoc ; then + uninstall_pandoc=1 + brew install pandoc +fi +find . -type f -name '*.html' -exec pandoc -f html -t markdown -o {}.md {} \; + +wayback_machine_downloader $WEB_SITE -l -a --from 2023-01-01 > wayback-urls.txt +wget --input-file=wayback-urls.txt --continue --wait=4 --retry-connrefused + +brew uninstall waybackpy +if [ -n "$uninstall_pandoc" ]; then + brew uninstall pandoc +fi +rm -rf wayback-machine-downloader +rm urls.txt wayback-urls.txt +~~~ diff --git a/public/about.html b/public/about.html new file mode 100644 index 0000000..b173595 --- /dev/null +++ b/public/about.html @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + +About – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

About

+ + +
+

BoochTek, LLC is a small web development company, based in St. Louis, Missouri. We specialize in Ruby, Ruby on Rails, Rails rescue projects, Agile methodologies, JavaScript, and HTML 5. We also do GNU/Linux system administration and network security.

+
+ + +
+ +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/author/booch.html b/public/author/booch.html new file mode 100644 index 0000000..1f08396 --- /dev/null +++ b/public/author/booch.html @@ -0,0 +1,481 @@ + + + + + + + + + + + + + + + +Craig Buchek – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

You Don’t Have to Be Right

+ + + +
+

Not too long ago, I was asked during a job interview “how do you convince your teammates that you’re right?” I answered with probably the most surprising answer: “I don’t.”

+

It’s taken me years to realize that being right isn’t terribly important. Especially when there’s more than one right answer — which there usually is. It’s more important to work as a team. It’s more important to be respected and to have respect for others.

+

A few weeks after that interview, I was pair programming with the person who had asked me the question. We’re both senior developers, so we both have a lot of experience and opinions based on that experience. We were writing a shell script, and we both had an idea in mind about how to write the code. I let him proceed with his idea. It was a pretty good idea; probably better than mine. But we paired very effectively. I’d let him finish some code, and I’d find a way to make it better. Then he’d find a way to make my code better. What we ended up with was so much better than my original idea, and his original idea as well. I commented on this, and he agreed. We left that pairing session feeling the high of having accomplished something rare — two very experience programmers coming up with better code than we could have imagined going in.

+

I like to think of writing code as a path towards a destination. The destination is good code that does what it’s supposed to, is readable (intention revealing), is concise, and is well-factored so as to be easy to change in the future. But there are many paths to that destination. Especially when pair programming, the code gets refined as ideas are shared, getting you closer and closer to that destination. Sometimes the different paths end up with the same code; sometimes they end up with different code. But if the code does its job well (according to those criteria), it doesn’t really matter what code you ended up with.

+

Thinking back to the original question, I think the answer is a little more nuanced. (I’m pretty sure I followed up with an explanation, but I don’t recall the details.) It truly doesn’t matter in a lot of cases. If there are different paths that lead to the same destination, and they’re all roughly equal, then my rule works. It can also work even if the first path you try (a “wrong” path) doesn’t work out, as long as it’s easy enough to try a different path. I find that the majority of day-to-day coding fits these conditions.

+

But there are some places where it’s costly to get things wrong the first time. In these cases, it’s worth spending some time thinking about and discussing the alternatives before getting started. Software architecture is the big one that comes to mind. In general, weighing the pros and cons of each option and applying previous experience works best.

+

So next time you think about trying to convince someone you’re right, fight the urge. Instead, let them show you what they’re thinking. They just might surprise you. And more importantly, you might surprise yourself.

+
+ + +
+ +
+
+ +

My First Open Space

+ + + +
+

I recently attended an Open Space hosted at work. I’d never been to an Open Space, and didn’t know what to expect. We’d been told that this was a workshop to help Engineering Managers (my role), Product Owners, and Product Analysts find better ways of working together. But due to the way an Open Space works, it evolved into something completely different — and better.

+

We’d brought in Diana Larsen to facilitate the Open Space. Diana is a stalwart of the Agile community, focusing on how people and teams interact. She literally (co-)wrote the book on Agile retrospectives. Diana was also kind enough to be our guest at an Agile LINC meetup later in the evening.

+

The morning started off with all the participants sitting in chairs arranged in a circle. Diana rang a chime, a nice soothing tone that literally set the tone for the day. (Most people didn’t seem to like it, but I thought it had its purpose.) It also acted as a call to gather in the meeting space. She then walked around the inside of the circle as she explained what was going to happen.

+

The idea behind Open Spaces came with the realization that at a conference, the “hallway track” (ad hoc discussions in the hallways) was often more valuable than the scheduled talks. So they figured out a way to capture that experience. There are only a few rules:

+
    +
  • Whoever shows up is the right group
  • +
  • Whatever happens is the only thing that could have
  • +
  • Be prepared to be surprised
  • +
  • Whenever it starts is the right time
  • +
  • When it’s over, it’s over
  • +
  • The Law of 2 Feet: If you’re not learning or contributing, go somewhere else
  • +
+

Once Diana set the scene and explained the rules, people came up and presented ideas for topics. If you proposed a topic, you chose a time and location on the topic board. At that time, you’d facilitate a discussion on that topic. The format was really conducive to discussions. There was no preparation, so discussions were from the heart — lots of people felt that they could contribute.

+

Being new to the company (only 2 months), I got a lot out of the workshop, beyond the content itself. I got to get to know several more people. I’d even say I made a few friends. Having open discussions, we found that many of the teams were having the same issues. This made it easy for me to talk to other people.

+

All-in-all, I found the Open Space to be extremely conducive to discussions aimed at identifying issues, brainstorming solutions, and planning action items. I felt like we made great strides in addressing some of the biggest problems our organization is currently facing.

+
+ + +
+ +
+
+ +

Resolutions for 2016

+ + + +
+

I’ve written up a list of career-related resolutions the past few years. I’ve been pretty successful meeting those goals, so I’m going to continue the tradition.

+

Coding

+

I want to increase the Open Source code I write this year. That’s basically everything I write that’s not directly related to work.

+

I took over maintenance of Virtus late last year, but haven’t done a good job at finding the time for that. That’s probably more responding to issues on GitHub and merging pull requests. But eventually, I’ll likely add some features and do some refactoring.

+

My goal is to work on 6 apps and libraries this year. That’s a pretty aggressive goal, but I want to focus on both learning new things and building some simple but useful things. Think MVP — focus on getting a lot of bang for not too much effort.

+

I want to work on a new Rails app, to familiarize myself with Rails 5. I want to get some experience with some more modern gems. I also want to see if I can use some new techniques with a Rails app, especially things that might help us get closer to a hexagonal architecture. I’m also hoping to work with some different web frameworks — maybe Lotus, Rodakase, Trailblazer, or Phoenix. Or maybe one of the Crystal web frameworks.

+

I also want to work on a couple Elm apps. I started the STL Elm group as an excuse to learn the language. I’d like to work on a Twitter client, that might eventually turn into a more general reading app. Beyond Twitter, it would keep track of things I want to read. The other Elm app I’d like to write is a game, based on the old Space Taxi game I loved to play on the Commodore 64.

+

I wrote a micro-ORM last year called Ruby Preserves. I’ve started converting that from working with raw SQL to sitting atop the awesome Sequel gem. The simplicity is still there so far, and basing it on Sequel has so far gone easier than I expected. But I haven’t figured out how to do relationships (JOINs) yet; that will be the real test.

+

Conferences

+

I’m pretty happy with my 2015 conference experience. I want to keep my conference level about the same. I don’t really want to commit to more talks than I gave last year. Conference presentations take a lot of time and energy to write and practice.

+

I’m planning to go to RailsConf, RubyConf, and Strange Loop. I’m hoping to give a talk at each of those, if I can. But I’ll probably attend all of them even if I’m not speaking. I’m also submitting a talk to Agile 2016. I’m unlikely to go to any other conferences, and even more unlikely to give more than 4 conference talks.

+

Writing

+

I really want to finish my Effective Agile book this year. I’m going to try to pull Amos in to pair with me on it occasionally, to move it forward. If I’m able to get it done, I’d also like to maybe write a book on Elm.

+

I actually want to do less blogging this year. Well, sort of. I want to put coding and book writing ahead of blogging. If that means less blogging, I’m okay with that. So my goal for blogging is more like every other week. And when I do write a blog article, I’d rather it be related to some code I’m working on, something that I could use in my book, or some more in-depth thoughts about programming language design.

+

Job Hunting

+

The contract I recently started at CenturyLink Cloud is open-ended, so I’ll likely be there for the entire year. I’m enjoying it so far; it’s a challenge, but I think I can make a significant impact. However, I’m really interested in moving to San Francisco in 2017. So I’m going to work on preparing for that in several ways.

+

I’m going to resurrect my LinkedIn account. I’ve neglected it for a few years now. I’ll make sure everything is up to date, and make some connections I’ve been putting off. I also want to reclaim my Stack Exchange and Hacker News accounts.

+

I’m going to pay a an expert to help me revamp my résumé. I think it’s pretty decent, but it could use some freshening up. I want it to stand out. I want to do a better job of explaining what I really do — which is to join a team to help them improve their processes and their technical skills.

+

I’ll also need to clean up all my web sites, so they look nice when people come to take a look at them. This includes my personal site, consulting business site, wiki, and blogs. I should also upgrade my server to the latest version of Debian, and use Ansible and some other tools to provision it using the principles of Infrastructure as Code.

+
+ + +
+ +
+
+ +

Team Values

+ + + +
+

I held a retrospective with my new team last week. The team includes 2 senior developers, 2 junior developers, a product owner, and a product analyst. I’ve joined the team as an engineering manager, which I think of more as a team lead with an elevated title. Being new to this group, I wanted a way to understand their values. What motivates them? What common values do we share that we can leverage to move forward in the same direction?

+

I started out with a pretty simple question: “What do you value (in regards to what we’re building); what are you willing to fight for?” I asked them each to write down several values and then put them on the board before looking at everyone else’s answers. I also asked each person to rank their values in order of importance.

+

It turned out that my question was a little vague. Some people thought about the question in terms of the end result, and some in terms of the process of creating the software. In some ways that was a bit of a problem, because the different interpretations led people in different directions. But in other ways, not pushing them in any particular direction got more varied answers, exposing how they think about the project and the product.

+

My list (in order) was Effectiveness (doing the right thing), Quality (doing things right the first time), Happiness, Purpose, and Teamwork. In retrospect, I should have put Happiness first. If I’m not happy at work, I don’t really want to be there, and need to move on. (Sometimes I can trade some happiness at work for more happiness at home, but I’m definitely a person that needs to be happy at work.) The other values serve to improve my happiness, but the happiness is more important.

+

My own issue ranking my values turned out to be a problem with the ranking in general. Should they be ranked in importance of the necessity of the value as an outcome, or in importance of the necessity to focus on the value? I think the former is what I was looking for, but even I wasn’t clear on that when I began the exercise.

+

After everyone put their values up on the board, we read them off. Then I asked the team to choose several values that we share as a team. We pulled them off the individual members’ lists, and put them in the team list. Then I asked each person to come up and rank those values, then explain why they had ordered them that way, especially when they ordered them significantly different than the last person. I was hoping to come to some convergence of the rank over time, so we could document our values in rank order. That didn’t happen. But the discussion was illuminating to me and to everyone on the team.

+

I think the most interesting part about the lack of convergence was the difference between the developers and the product guys. The product guys definitely viewed the values more in terms of outcomes than the process. That makes sense — they’re not as intimately involved in the process of building the product.

+

We were able to converge on the top priority though: Will the user buy the product? This was a combination of a couple different values that we merged together. This included the end user experience as well as making sure the team would continue to have a reason for existing. The rest of the values we left unordered: Teamwork (cohesiveness), Data driven decisions, Team ownership, Simplicity, Effectiveness, Maintainability / Supportability, Quality, Automation, and Performance. I think that’s a pretty decent list.

+

As a couple teammates pointed out, those values are probably in part a reflection of this current point in time. If I asked the same question some other time, under different conditions and team dynamics, the answers would probably change a bit. And we’d probably come up with other answers if asked again, just due to randomness of the way we think about these things.

+

But I don’t think I’d do this activity a second time with the team. It was really about understanding our motivations — both our own, and those of our teammates. I found it effective in that way, and also in helping the team to think about our culture and how we can work to shape it to help us all push in the same direction.

+

There are a few caveats. When I asked for feedback on the exercise, one teammate pointed out that it wouldn’t work if people weren’t honest about their values, and they answered with what they thought management or their teammates wanted to hear. I don’t think that was an issue with this group, but it’s something to keep in mind.

+

The other major thing I’d do is to make it clear up front that I’m not looking for any action items from this activity; it’s more about understanding each other and ourselves. And next time, I’ll work to clarify how to rank the values.

+

I would recommend this activity for a new team, or when the makeup of the team is changing in some significant way. I wish there was a way to help a team converge on the ranking of their values, but I suppose I should be happy that agreeing on the set of important values went pretty quickly. And the diversity of ideas and opinions is probably a blessing that I should be embracing — the more ideas we have, the wider the variety of solutions we can imagine.

+
+ + +
+ +
+
+ +

2015 Year in Review

+ + + +
+

It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+
+ + +
+ +
+
+ +

Face Your Fears

+ + + +
+

I’ve always been someone who faces my fears.

+

I have a moderate case of arachnophobia. I don’t run away when I see a spider, but it creeps my out when one is crawling on me. When I was in college, I decided to buy a tarantula to attempt to get over my irrational fear of spiders. I thought I’d be able to get more comfortable with the tarantula over time, eventually to the point of letting it crawl on my arm. It didn’t work. Although I did find that my fear of tarantulas is rational — I got a terrible case of hives just from touching the urticating hairs that fell off into its water sponge.

+

Last week, I was on vacation in Mexico. One of the excursions we took involved jumping in the water a lot. I’m not a strong swimmer — mostly because I have a hard time closing my nose; I hold my nose when I jump in. We zip-lined into the water a lot. At one point, there was a cliff to dive into the water from. It was about 15 feet above the water. It didn’t look so far down before jumping. But it felt like a really long way down the first time I jumped from it. It was pretty scary for me. So I did it a second time. There wasn’t any peer pressure to jump a second time. I literally jumped a second time specifically because I was scared.

+

Fear is a weird thing. Fear is there to protect us. But it’s there to protect us from the dangers of the African savannah. Most of the things our fears protect us from don’t exist in our everyday modern lives. So maybe we should work to gain a better understanding of how our fears work, to figure out when to pay attention to them and when to ignore them.

+

Fortunately, our brain has a good mechanism to help us do this. Our brains basically have 2 main processing systems. The first one is for quick reactions. This one involves things that are nearly reflexes. Fear is in this system. The second system is our analytical reasoning system. This system takes longer to process, but is able to take on more information.

+

Whenever the situation allows us time for both systems to work, we need to listen to them both. We need to listen to our fears, because they’re there for a reason. But that reason might not pertain to our situation. So we need to realize that, and let the slow analytical system determine if we should ignore our fears.

+

If we don’t allow both systems to work, we’re not taking full advantage of our brains; we’re not taking full advantage of the situations that life is presenting to us.

+
+ + +
+ +
+
+ +

Encouragement

+ + + +
+

I’ve been on vacation the past week, in Cozumel, Mexico. One day, we went on an excursion called Xenotes. A cenote (say-NO-tay) is a sinkhole filled with fresh water. (The “X” is to give it a more Mayan-sounding trademarkable name.) We had a lot of fun swimming, kayaking, and zip-lining. A bilingual tour guide led our group, which consisted of people from across the US and South America, of various ages and physical abilities.

+

Our tour guide was a lot of fun. He made jokes, told us about the cenotes, and led us in the activities. But he also encouraged us. When someone was scared to do something, he was supportive. He told us that it was okay, and we could do it. But we also felt like it was OK to fail, if we really couldn’t. It really felt like his encouragement was literally creating courage.

+

What was really neat was that despite the language barrier, everyone else was also supportive and encouraging. Everyone cheered with encouragement before someone would attempt something difficult. And we’d cheer especially loud once someone accomplished something that was difficult for their abilities.

+

It was an awesome feeling to feel so supported. It made me feel like I was in a safe place, where I could try new things that were a little past my comfort zone. I was able to do the zip-line upside-down. I jumped off a 15-foot cliff into the water. I even jumped off the cliff a second time, even though I was a little scared.

+

Back at the resort, we played some volleyball in the pool. It was a similar situation, with players of varying ages and abilities. Again, we tried to help the weaker players feel comfortable so they could improve without feeling judged or self-conscious. It helped everyone have a good time. It made everything more fun to be in such a supportive environment, whether I was in a position as one of the more skilled (volleyball), or one of the less skilled (jumping or zip-lining into the water). We were all able to accomplish more, with less effort.

+

These experiences provide a good lesson that can be applied in a lot of places. Such a supportive environment would help any relationship, and any team. I’ve been on a couple really good software development teams, but I don’t think any of them have been as supportive as these two groups of strangers.

+

I’ve decided that this should be one of my goals as a team leader. I want to create an encouraging environment for the whole team. I want to make sure that everyone is comfortable enough that they feel like they can try things that are difficult, even if they might fail (as long as nobody gets hurt).

+

If a group of strangers can do this, so can your team. So can you and your significant others. We need to work every day to make sure that we’re supporting each other. It’s the best way to get everyone to achieve more.

+
+ + +
+ +
+
+ +

The Ultimate Optimization

+ + + +
+

I got my first computer in 1984. It was a Commodore 64. I had to do extra chores and save up my allowance to buy it. I was 13.

+

Back then, Sears and other retail stores had Commodore 64 computers out on display. Whenever I went to the store, I’d write a short BASIC program and leave it running on the display computers:

+
10 PRINT "CRAIG IS GREAT!"
+20 GOTO 10
+

Hey, I was a 13-year-old kid. Later, I got a little more sophisticated. I’d change the background color instead. I still remember the POKE address:

+
10 FOR I=0 TO 255
+20 POKE 53281, I
+30 NEXT I
+40 GOTO 10
+RUN
+

This was fast enough to change the background color every few scan lines, creating a flashing scrolling effect.

+

Later, I learned 6502 assembly language. I translated the BASIC program into assembler, and memorized the bytes to type in at the store. In assembly language, the background color would change several times per scan line. The effect was kind of psychedelic.

+

All that happened in the mid-1980s.

+

Fast-forward to about 2000 or so. I was telling the above story after a St. Louis LUG meeting. I explained how I had memorized the 10 or 12 bytes of machine code, and would leave the program running with its psychedelic effect.

+

After thinking about it for a bit, I thought that 10 or 12 bytes seemed too much. It actually bothered me — I couldn’t fall asleep when I got home. I got up and found my old 6502 manuals. I figured out how to write the code in 7 bytes. I installed the Vice C64 emulator on my Linux desktop, and tested my code. It worked as expected. (The emulator was already clock-cycle perfect by then.) Here’s the assembly code:

+
INX         ; $E8           ; 232
+STX $D021   ; $8E $21 $D0   ; 142 33 208   ; $D021 = 53281
+JMP $C000   ; $4C $00 $C0   ; 76 0 192     ; $C000 = 49152
+

Here’s the BASIC program to store that program in memory and run it:

+
10 FOR N=49152 TO 49152+6: READ Q : POKE N, Q : NEXT
+20 DATA 232, 142, 33, 208, 76, 0, 192
+30 SYS 49152
+RUN
+

The moral of the story is that you can optimize even a 10-byte program, 15 years after the last time it was used. So don’t tell me that your program can’t be improved, no matter how small it is.

+

PS. I rewrote the code above while writing this article in 2015, about 15 years after the last time I rewrote it. And I again downloaded Vice to test it, this time on Mac OS X.

+
+ + +
+ +
+
+ +

Show and Tell

+ + + +
+

I’m wrapping up my current consulting gig at Mercy in December, and starting a new gig at CenturyLink. I’m a software developer, but that’s only a part of what I do. What I really do is join a team and help them improve the way they work — both their processes and their technical skills.

+

I think this is a key differentiator for me as a consultant. Most consultants (and Agile coaches) come in and tell people what to do. I don’t like to just tell people what to do. I’d much prefer to work side-by-side with them, getting a better understanding of what their challenges are. Once I have a better understanding of the challenges, I’m able to better brainstorm some ideas to try. Then we can experiment to see what will work and what won’t.

+

Instead of telling people what to do, I show them how. Most people learn better from seeing than from hearing. They also learn better if you explain how and why, not just what. So showing them how to do something is more effective than telling them. By showing and doing, you can also set a good example. This is especially important when collaboration is a large part of what needs to be improved.

+

I’ve found that this style of consulting is more highly respected by everyone. I build trust with developers by working closely with them. Managers like to keep me around once they see how effective these methods can be, so the gigs I take on tend to last relatively long.

+

The biggest problem I have is explaining how this works. I don’t really know what to put on my résumé. Sometimes I call myself an Agile practitioner, and sometimes an Agile player/coach. But those aren’t terribly satisfying descriptions. I’m considering actually putting “I help teams improve the way they work — both their processes and their technical skills” on the résumé. But that seems awkward, and misses the show versus tell part. I’d be open to any suggestions.

+

 

+
+ + +
+ +
+
+ +

Happiness Retrospective

+ + + +
+

I facilitated a retrospective today; it was one of the best retros I’ve ever been involved with. I figured out what activities I wanted to do earlier in the morning. They were really quite simple. I wanted to focus on happiness.

+

How happy are you at work?

+

I started with two questions that I’ve used with teams before, to some success (although not so successful for one particular team). The first question I asked was “How happy are you at work?” I had them put a rating from 0 to 10, with 0 meaning they should have quit last week, and 10 meaning they couldn’t imaging being happier at work.

+

The answers were mostly 7s and 8s, with a 5 and a 9. The average came to 7.5, which is pretty good. The 5 concerns me a bit, especially that it’s 2 points lower than anyone else’s answer.

+

How effective do you think the teams is?

+

The next question I asked was “How effective do you think the teams is?”. Again, from 0 to 10, with 0 meaning they can’t accomplish anything, and 10 meaning you couldn’t imagine a more effective team.

+

When I ask both of these questions, the scores are always highly correlated. If your team isn’t doing good work, it’ll make you unhappy. And if you are unhappy, you’re less likely to do your best work. This team was no different; the 5 and 9 became a 6 and a 10, and most of the 7s became 8s, for an average of just under 8.

+

What makes you happy at work?

+

The next question I asked was “What makes you happy at work?”. The answers were mostly about the teamwork and teammates. This went quicker than I expected. At this point, I was worried the retro would only last a little more than 30 minutes.

+

What would make you happier at work?

+

The final question I asked was “What would make you happier at work?”. This was the real pay-off. We spent about half an hour just talking about the things that would make us happier, and what we could do to improve our happiness. We came up with 10 potential action items. I usually limit teams to trying 3 or 4 action items, but most of the items are quite small, so we’re going to try 6 of them. One is just observing another team’s standup meetings, to see how they’re using their time effectively.

+

Everyone went away feeling that this was a really good retro. It felt good to focus on happiness. Happiness is something I’ve been talking a lot about on the This Agile Life podcast, and it felt good to take some action on it. I’ve done “positive-only” retros before, but this one felt even better than that, by specifically targeting happiness and how we can achieve it.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/author/booch/feed b/public/author/booch/feed new file mode 100644 index 0000000..632d015 --- /dev/null +++ b/public/author/booch/feed @@ -0,0 +1,311 @@ + + + + Craig Buchek – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + You Don’t Have to Be Right + http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right + http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right#respond + Thu, 07 Jul 2016 04:22:08 +0000 + + + + + http://blog.boochtek.com/?p=313 + Continue reading "You Don’t Have to Be Right"]]> + Not too long ago, I was asked during a job interview “how do you convince your teammates that you’re right?” I answered with probably the most surprising answer: “I don’t.”

+

It’s taken me years to realize that being right isn’t terribly important. Especially when there’s more than one right answer — which there usually is. It’s more important to work as a team. It’s more important to be respected and to have respect for others.

+

A few weeks after that interview, I was pair programming with the person who had asked me the question. We’re both senior developers, so we both have a lot of experience and opinions based on that experience. We were writing a shell script, and we both had an idea in mind about how to write the code. I let him proceed with his idea. It was a pretty good idea; probably better than mine. But we paired very effectively. I’d let him finish some code, and I’d find a way to make it better. Then he’d find a way to make my code better. What we ended up with was so much better than my original idea, and his original idea as well. I commented on this, and he agreed. We left that pairing session feeling the high of having accomplished something rare — two very experience programmers coming up with better code than we could have imagined going in.

+

I like to think of writing code as a path towards a destination. The destination is good code that does what it’s supposed to, is readable (intention revealing), is concise, and is well-factored so as to be easy to change in the future. But there are many paths to that destination. Especially when pair programming, the code gets refined as ideas are shared, getting you closer and closer to that destination. Sometimes the different paths end up with the same code; sometimes they end up with different code. But if the code does its job well (according to those criteria), it doesn’t really matter what code you ended up with.

+

Thinking back to the original question, I think the answer is a little more nuanced. (I’m pretty sure I followed up with an explanation, but I don’t recall the details.) It truly doesn’t matter in a lot of cases. If there are different paths that lead to the same destination, and they’re all roughly equal, then my rule works. It can also work even if the first path you try (a “wrong” path) doesn’t work out, as long as it’s easy enough to try a different path. I find that the majority of day-to-day coding fits these conditions.

+

But there are some places where it’s costly to get things wrong the first time. In these cases, it’s worth spending some time thinking about and discussing the alternatives before getting started. Software architecture is the big one that comes to mind. In general, weighing the pros and cons of each option and applying previous experience works best.

+

So next time you think about trying to convince someone you’re right, fight the urge. Instead, let them show you what they’re thinking. They just might surprise you. And more importantly, you might surprise yourself.

+]]>
+ http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right/feed + 0 +
+ + My First Open Space + http://blog.boochtek.com/2016/02/10/first-open-space + http://blog.boochtek.com/2016/02/10/first-open-space#respond + Thu, 11 Feb 2016 05:42:54 +0000 + + + + http://blog.boochtek.com/?p=307 + Continue reading "My First Open Space"]]> + I recently attended an Open Space hosted at work. I’d never been to an Open Space, and didn’t know what to expect. We’d been told that this was a workshop to help Engineering Managers (my role), Product Owners, and Product Analysts find better ways of working together. But due to the way an Open Space works, it evolved into something completely different — and better.

+

We’d brought in Diana Larsen to facilitate the Open Space. Diana is a stalwart of the Agile community, focusing on how people and teams interact. She literally (co-)wrote the book on Agile retrospectives. Diana was also kind enough to be our guest at an Agile LINC meetup later in the evening.

+

The morning started off with all the participants sitting in chairs arranged in a circle. Diana rang a chime, a nice soothing tone that literally set the tone for the day. (Most people didn’t seem to like it, but I thought it had its purpose.) It also acted as a call to gather in the meeting space. She then walked around the inside of the circle as she explained what was going to happen.

+

The idea behind Open Spaces came with the realization that at a conference, the “hallway track” (ad hoc discussions in the hallways) was often more valuable than the scheduled talks. So they figured out a way to capture that experience. There are only a few rules:

+
    +
  • Whoever shows up is the right group
  • +
  • Whatever happens is the only thing that could have
  • +
  • Be prepared to be surprised
  • +
  • Whenever it starts is the right time
  • +
  • When it’s over, it’s over
  • +
  • The Law of 2 Feet: If you’re not learning or contributing, go somewhere else
  • +
+

Once Diana set the scene and explained the rules, people came up and presented ideas for topics. If you proposed a topic, you chose a time and location on the topic board. At that time, you’d facilitate a discussion on that topic. The format was really conducive to discussions. There was no preparation, so discussions were from the heart — lots of people felt that they could contribute.

+

Being new to the company (only 2 months), I got a lot out of the workshop, beyond the content itself. I got to get to know several more people. I’d even say I made a few friends. Having open discussions, we found that many of the teams were having the same issues. This made it easy for me to talk to other people.

+

All-in-all, I found the Open Space to be extremely conducive to discussions aimed at identifying issues, brainstorming solutions, and planning action items. I felt like we made great strides in addressing some of the biggest problems our organization is currently facing.

+]]>
+ http://blog.boochtek.com/2016/02/10/first-open-space/feed + 0 +
+ + Resolutions for 2016 + http://blog.boochtek.com/2016/01/19/resolutions-2016 + http://blog.boochtek.com/2016/01/19/resolutions-2016#respond + Wed, 20 Jan 2016 05:51:09 +0000 + + + + http://blog.boochtek.com/?p=290 + Continue reading "Resolutions for 2016"]]> + I’ve written up a list of career-related resolutions the past few years. I’ve been pretty successful meeting those goals, so I’m going to continue the tradition.

+

Coding

+

I want to increase the Open Source code I write this year. That’s basically everything I write that’s not directly related to work.

+

I took over maintenance of Virtus late last year, but haven’t done a good job at finding the time for that. That’s probably more responding to issues on GitHub and merging pull requests. But eventually, I’ll likely add some features and do some refactoring.

+

My goal is to work on 6 apps and libraries this year. That’s a pretty aggressive goal, but I want to focus on both learning new things and building some simple but useful things. Think MVP — focus on getting a lot of bang for not too much effort.

+

I want to work on a new Rails app, to familiarize myself with Rails 5. I want to get some experience with some more modern gems. I also want to see if I can use some new techniques with a Rails app, especially things that might help us get closer to a hexagonal architecture. I’m also hoping to work with some different web frameworks — maybe Lotus, Rodakase, Trailblazer, or Phoenix. Or maybe one of the Crystal web frameworks.

+

I also want to work on a couple Elm apps. I started the STL Elm group as an excuse to learn the language. I’d like to work on a Twitter client, that might eventually turn into a more general reading app. Beyond Twitter, it would keep track of things I want to read. The other Elm app I’d like to write is a game, based on the old Space Taxi game I loved to play on the Commodore 64.

+

I wrote a micro-ORM last year called Ruby Preserves. I’ve started converting that from working with raw SQL to sitting atop the awesome Sequel gem. The simplicity is still there so far, and basing it on Sequel has so far gone easier than I expected. But I haven’t figured out how to do relationships (JOINs) yet; that will be the real test.

+

Conferences

+

I’m pretty happy with my 2015 conference experience. I want to keep my conference level about the same. I don’t really want to commit to more talks than I gave last year. Conference presentations take a lot of time and energy to write and practice.

+

I’m planning to go to RailsConf, RubyConf, and Strange Loop. I’m hoping to give a talk at each of those, if I can. But I’ll probably attend all of them even if I’m not speaking. I’m also submitting a talk to Agile 2016. I’m unlikely to go to any other conferences, and even more unlikely to give more than 4 conference talks.

+

Writing

+

I really want to finish my Effective Agile book this year. I’m going to try to pull Amos in to pair with me on it occasionally, to move it forward. If I’m able to get it done, I’d also like to maybe write a book on Elm.

+

I actually want to do less blogging this year. Well, sort of. I want to put coding and book writing ahead of blogging. If that means less blogging, I’m okay with that. So my goal for blogging is more like every other week. And when I do write a blog article, I’d rather it be related to some code I’m working on, something that I could use in my book, or some more in-depth thoughts about programming language design.

+

Job Hunting

+

The contract I recently started at CenturyLink Cloud is open-ended, so I’ll likely be there for the entire year. I’m enjoying it so far; it’s a challenge, but I think I can make a significant impact. However, I’m really interested in moving to San Francisco in 2017. So I’m going to work on preparing for that in several ways.

+

I’m going to resurrect my LinkedIn account. I’ve neglected it for a few years now. I’ll make sure everything is up to date, and make some connections I’ve been putting off. I also want to reclaim my Stack Exchange and Hacker News accounts.

+

I’m going to pay a an expert to help me revamp my résumé. I think it’s pretty decent, but it could use some freshening up. I want it to stand out. I want to do a better job of explaining what I really do — which is to join a team to help them improve their processes and their technical skills.

+

I’ll also need to clean up all my web sites, so they look nice when people come to take a look at them. This includes my personal site, consulting business site, wiki, and blogs. I should also upgrade my server to the latest version of Debian, and use Ansible and some other tools to provision it using the principles of Infrastructure as Code.

+]]>
+ http://blog.boochtek.com/2016/01/19/resolutions-2016/feed + 0 +
+ + Team Values + http://blog.boochtek.com/2016/01/04/team-values + http://blog.boochtek.com/2016/01/04/team-values#respond + Tue, 05 Jan 2016 04:56:46 +0000 + + + + + http://blog.boochtek.com/?p=294 + Continue reading "Team Values"]]> + I held a retrospective with my new team last week. The team includes 2 senior developers, 2 junior developers, a product owner, and a product analyst. I’ve joined the team as an engineering manager, which I think of more as a team lead with an elevated title. Being new to this group, I wanted a way to understand their values. What motivates them? What common values do we share that we can leverage to move forward in the same direction?

+

I started out with a pretty simple question: “What do you value (in regards to what we’re building); what are you willing to fight for?” I asked them each to write down several values and then put them on the board before looking at everyone else’s answers. I also asked each person to rank their values in order of importance.

+

It turned out that my question was a little vague. Some people thought about the question in terms of the end result, and some in terms of the process of creating the software. In some ways that was a bit of a problem, because the different interpretations led people in different directions. But in other ways, not pushing them in any particular direction got more varied answers, exposing how they think about the project and the product.

+

My list (in order) was Effectiveness (doing the right thing), Quality (doing things right the first time), Happiness, Purpose, and Teamwork. In retrospect, I should have put Happiness first. If I’m not happy at work, I don’t really want to be there, and need to move on. (Sometimes I can trade some happiness at work for more happiness at home, but I’m definitely a person that needs to be happy at work.) The other values serve to improve my happiness, but the happiness is more important.

+

My own issue ranking my values turned out to be a problem with the ranking in general. Should they be ranked in importance of the necessity of the value as an outcome, or in importance of the necessity to focus on the value? I think the former is what I was looking for, but even I wasn’t clear on that when I began the exercise.

+

After everyone put their values up on the board, we read them off. Then I asked the team to choose several values that we share as a team. We pulled them off the individual members’ lists, and put them in the team list. Then I asked each person to come up and rank those values, then explain why they had ordered them that way, especially when they ordered them significantly different than the last person. I was hoping to come to some convergence of the rank over time, so we could document our values in rank order. That didn’t happen. But the discussion was illuminating to me and to everyone on the team.

+

I think the most interesting part about the lack of convergence was the difference between the developers and the product guys. The product guys definitely viewed the values more in terms of outcomes than the process. That makes sense — they’re not as intimately involved in the process of building the product.

+

We were able to converge on the top priority though: Will the user buy the product? This was a combination of a couple different values that we merged together. This included the end user experience as well as making sure the team would continue to have a reason for existing. The rest of the values we left unordered: Teamwork (cohesiveness), Data driven decisions, Team ownership, Simplicity, Effectiveness, Maintainability / Supportability, Quality, Automation, and Performance. I think that’s a pretty decent list.

+

As a couple teammates pointed out, those values are probably in part a reflection of this current point in time. If I asked the same question some other time, under different conditions and team dynamics, the answers would probably change a bit. And we’d probably come up with other answers if asked again, just due to randomness of the way we think about these things.

+

But I don’t think I’d do this activity a second time with the team. It was really about understanding our motivations — both our own, and those of our teammates. I found it effective in that way, and also in helping the team to think about our culture and how we can work to shape it to help us all push in the same direction.

+

There are a few caveats. When I asked for feedback on the exercise, one teammate pointed out that it wouldn’t work if people weren’t honest about their values, and they answered with what they thought management or their teammates wanted to hear. I don’t think that was an issue with this group, but it’s something to keep in mind.

+

The other major thing I’d do is to make it clear up front that I’m not looking for any action items from this activity; it’s more about understanding each other and ourselves. And next time, I’ll work to clarify how to rank the values.

+

I would recommend this activity for a new team, or when the makeup of the team is changing in some significant way. I wish there was a way to help a team converge on the ranking of their values, but I suppose I should be happy that agreeing on the set of important values went pretty quickly. And the diversity of ideas and opinions is probably a blessing that I should be embracing — the more ideas we have, the wider the variety of solutions we can imagine.

+]]>
+ http://blog.boochtek.com/2016/01/04/team-values/feed + 0 +
+ + 2015 Year in Review + http://blog.boochtek.com/2015/12/28/year-in-review-2015 + http://blog.boochtek.com/2015/12/28/year-in-review-2015#respond + Tue, 29 Dec 2015 05:28:38 +0000 + + + + + + + + http://blog.boochtek.com/?p=287 + Continue reading "2015 Year in Review"]]> + It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+]]>
+ http://blog.boochtek.com/2015/12/28/year-in-review-2015/feed + 0 +
+ + Face Your Fears + http://blog.boochtek.com/2015/12/21/face-your-fears + http://blog.boochtek.com/2015/12/21/face-your-fears#respond + Tue, 22 Dec 2015 05:38:29 +0000 + + + + http://blog.boochtek.com/?p=285 + Continue reading "Face Your Fears"]]> + I’ve always been someone who faces my fears.

+

I have a moderate case of arachnophobia. I don’t run away when I see a spider, but it creeps my out when one is crawling on me. When I was in college, I decided to buy a tarantula to attempt to get over my irrational fear of spiders. I thought I’d be able to get more comfortable with the tarantula over time, eventually to the point of letting it crawl on my arm. It didn’t work. Although I did find that my fear of tarantulas is rational — I got a terrible case of hives just from touching the urticating hairs that fell off into its water sponge.

+

Last week, I was on vacation in Mexico. One of the excursions we took involved jumping in the water a lot. I’m not a strong swimmer — mostly because I have a hard time closing my nose; I hold my nose when I jump in. We zip-lined into the water a lot. At one point, there was a cliff to dive into the water from. It was about 15 feet above the water. It didn’t look so far down before jumping. But it felt like a really long way down the first time I jumped from it. It was pretty scary for me. So I did it a second time. There wasn’t any peer pressure to jump a second time. I literally jumped a second time specifically because I was scared.

+

Fear is a weird thing. Fear is there to protect us. But it’s there to protect us from the dangers of the African savannah. Most of the things our fears protect us from don’t exist in our everyday modern lives. So maybe we should work to gain a better understanding of how our fears work, to figure out when to pay attention to them and when to ignore them.

+

Fortunately, our brain has a good mechanism to help us do this. Our brains basically have 2 main processing systems. The first one is for quick reactions. This one involves things that are nearly reflexes. Fear is in this system. The second system is our analytical reasoning system. This system takes longer to process, but is able to take on more information.

+

Whenever the situation allows us time for both systems to work, we need to listen to them both. We need to listen to our fears, because they’re there for a reason. But that reason might not pertain to our situation. So we need to realize that, and let the slow analytical system determine if we should ignore our fears.

+

If we don’t allow both systems to work, we’re not taking full advantage of our brains; we’re not taking full advantage of the situations that life is presenting to us.

+]]>
+ http://blog.boochtek.com/2015/12/21/face-your-fears/feed + 0 +
+ + Encouragement + http://blog.boochtek.com/2015/12/15/encouragement + http://blog.boochtek.com/2015/12/15/encouragement#respond + Wed, 16 Dec 2015 05:00:22 +0000 + + + + http://blog.boochtek.com/?p=282 + Continue reading "Encouragement"]]> + I’ve been on vacation the past week, in Cozumel, Mexico. One day, we went on an excursion called Xenotes. A cenote (say-NO-tay) is a sinkhole filled with fresh water. (The “X” is to give it a more Mayan-sounding trademarkable name.) We had a lot of fun swimming, kayaking, and zip-lining. A bilingual tour guide led our group, which consisted of people from across the US and South America, of various ages and physical abilities.

+

Our tour guide was a lot of fun. He made jokes, told us about the cenotes, and led us in the activities. But he also encouraged us. When someone was scared to do something, he was supportive. He told us that it was okay, and we could do it. But we also felt like it was OK to fail, if we really couldn’t. It really felt like his encouragement was literally creating courage.

+

What was really neat was that despite the language barrier, everyone else was also supportive and encouraging. Everyone cheered with encouragement before someone would attempt something difficult. And we’d cheer especially loud once someone accomplished something that was difficult for their abilities.

+

It was an awesome feeling to feel so supported. It made me feel like I was in a safe place, where I could try new things that were a little past my comfort zone. I was able to do the zip-line upside-down. I jumped off a 15-foot cliff into the water. I even jumped off the cliff a second time, even though I was a little scared.

+

Back at the resort, we played some volleyball in the pool. It was a similar situation, with players of varying ages and abilities. Again, we tried to help the weaker players feel comfortable so they could improve without feeling judged or self-conscious. It helped everyone have a good time. It made everything more fun to be in such a supportive environment, whether I was in a position as one of the more skilled (volleyball), or one of the less skilled (jumping or zip-lining into the water). We were all able to accomplish more, with less effort.

+

These experiences provide a good lesson that can be applied in a lot of places. Such a supportive environment would help any relationship, and any team. I’ve been on a couple really good software development teams, but I don’t think any of them have been as supportive as these two groups of strangers.

+

I’ve decided that this should be one of my goals as a team leader. I want to create an encouraging environment for the whole team. I want to make sure that everyone is comfortable enough that they feel like they can try things that are difficult, even if they might fail (as long as nobody gets hurt).

+

If a group of strangers can do this, so can your team. So can you and your significant others. We need to work every day to make sure that we’re supporting each other. It’s the best way to get everyone to achieve more.

+]]>
+ http://blog.boochtek.com/2015/12/15/encouragement/feed + 0 +
+ + The Ultimate Optimization + http://blog.boochtek.com/2015/12/06/ultimate-optimization + http://blog.boochtek.com/2015/12/06/ultimate-optimization#respond + Mon, 07 Dec 2015 05:28:43 +0000 + + + + http://blog.boochtek.com/?p=277 + Continue reading "The Ultimate Optimization"]]> + I got my first computer in 1984. It was a Commodore 64. I had to do extra chores and save up my allowance to buy it. I was 13.

+

Back then, Sears and other retail stores had Commodore 64 computers out on display. Whenever I went to the store, I’d write a short BASIC program and leave it running on the display computers:

+
10 PRINT "CRAIG IS GREAT!"
+20 GOTO 10
+

Hey, I was a 13-year-old kid. Later, I got a little more sophisticated. I’d change the background color instead. I still remember the POKE address:

+
10 FOR I=0 TO 255
+20 POKE 53281, I
+30 NEXT I
+40 GOTO 10
+RUN
+

This was fast enough to change the background color every few scan lines, creating a flashing scrolling effect.

+

Later, I learned 6502 assembly language. I translated the BASIC program into assembler, and memorized the bytes to type in at the store. In assembly language, the background color would change several times per scan line. The effect was kind of psychedelic.

+

All that happened in the mid-1980s.

+

Fast-forward to about 2000 or so. I was telling the above story after a St. Louis LUG meeting. I explained how I had memorized the 10 or 12 bytes of machine code, and would leave the program running with its psychedelic effect.

+

After thinking about it for a bit, I thought that 10 or 12 bytes seemed too much. It actually bothered me — I couldn’t fall asleep when I got home. I got up and found my old 6502 manuals. I figured out how to write the code in 7 bytes. I installed the Vice C64 emulator on my Linux desktop, and tested my code. It worked as expected. (The emulator was already clock-cycle perfect by then.) Here’s the assembly code:

+
INX         ; $E8           ; 232
+STX $D021   ; $8E $21 $D0   ; 142 33 208   ; $D021 = 53281
+JMP $C000   ; $4C $00 $C0   ; 76 0 192     ; $C000 = 49152
+

Here’s the BASIC program to store that program in memory and run it:

+
10 FOR N=49152 TO 49152+6: READ Q : POKE N, Q : NEXT
+20 DATA 232, 142, 33, 208, 76, 0, 192
+30 SYS 49152
+RUN
+

The moral of the story is that you can optimize even a 10-byte program, 15 years after the last time it was used. So don’t tell me that your program can’t be improved, no matter how small it is.

+

PS. I rewrote the code above while writing this article in 2015, about 15 years after the last time I rewrote it. And I again downloaded Vice to test it, this time on Mac OS X.

+]]>
+ http://blog.boochtek.com/2015/12/06/ultimate-optimization/feed + 0 +
+ + Show and Tell + http://blog.boochtek.com/2015/11/24/show-and-tell + http://blog.boochtek.com/2015/11/24/show-and-tell#respond + Wed, 25 Nov 2015 05:21:06 +0000 + + + + http://blog.boochtek.com/?p=271 + Continue reading "Show and Tell"]]> + I’m wrapping up my current consulting gig at Mercy in December, and starting a new gig at CenturyLink. I’m a software developer, but that’s only a part of what I do. What I really do is join a team and help them improve the way they work — both their processes and their technical skills.

+

I think this is a key differentiator for me as a consultant. Most consultants (and Agile coaches) come in and tell people what to do. I don’t like to just tell people what to do. I’d much prefer to work side-by-side with them, getting a better understanding of what their challenges are. Once I have a better understanding of the challenges, I’m able to better brainstorm some ideas to try. Then we can experiment to see what will work and what won’t.

+

Instead of telling people what to do, I show them how. Most people learn better from seeing than from hearing. They also learn better if you explain how and why, not just what. So showing them how to do something is more effective than telling them. By showing and doing, you can also set a good example. This is especially important when collaboration is a large part of what needs to be improved.

+

I’ve found that this style of consulting is more highly respected by everyone. I build trust with developers by working closely with them. Managers like to keep me around once they see how effective these methods can be, so the gigs I take on tend to last relatively long.

+

The biggest problem I have is explaining how this works. I don’t really know what to put on my résumé. Sometimes I call myself an Agile practitioner, and sometimes an Agile player/coach. But those aren’t terribly satisfying descriptions. I’m considering actually putting “I help teams improve the way they work — both their processes and their technical skills” on the résumé. But that seems awkward, and misses the show versus tell part. I’d be open to any suggestions.

+

 

+]]>
+ http://blog.boochtek.com/2015/11/24/show-and-tell/feed + 0 +
+ + Happiness Retrospective + http://blog.boochtek.com/2015/11/09/happiness-retrospective + http://blog.boochtek.com/2015/11/09/happiness-retrospective#respond + Tue, 10 Nov 2015 05:56:58 +0000 + + + + + http://blog.boochtek.com/?p=268 + Continue reading "Happiness Retrospective"]]> + I facilitated a retrospective today; it was one of the best retros I’ve ever been involved with. I figured out what activities I wanted to do earlier in the morning. They were really quite simple. I wanted to focus on happiness.

+

How happy are you at work?

+

I started with two questions that I’ve used with teams before, to some success (although not so successful for one particular team). The first question I asked was “How happy are you at work?” I had them put a rating from 0 to 10, with 0 meaning they should have quit last week, and 10 meaning they couldn’t imaging being happier at work.

+

The answers were mostly 7s and 8s, with a 5 and a 9. The average came to 7.5, which is pretty good. The 5 concerns me a bit, especially that it’s 2 points lower than anyone else’s answer.

+

How effective do you think the teams is?

+

The next question I asked was “How effective do you think the teams is?”. Again, from 0 to 10, with 0 meaning they can’t accomplish anything, and 10 meaning you couldn’t imagine a more effective team.

+

When I ask both of these questions, the scores are always highly correlated. If your team isn’t doing good work, it’ll make you unhappy. And if you are unhappy, you’re less likely to do your best work. This team was no different; the 5 and 9 became a 6 and a 10, and most of the 7s became 8s, for an average of just under 8.

+

What makes you happy at work?

+

The next question I asked was “What makes you happy at work?”. The answers were mostly about the teamwork and teammates. This went quicker than I expected. At this point, I was worried the retro would only last a little more than 30 minutes.

+

What would make you happier at work?

+

The final question I asked was “What would make you happier at work?”. This was the real pay-off. We spent about half an hour just talking about the things that would make us happier, and what we could do to improve our happiness. We came up with 10 potential action items. I usually limit teams to trying 3 or 4 action items, but most of the items are quite small, so we’re going to try 6 of them. One is just observing another team’s standup meetings, to see how they’re using their time effectively.

+

Everyone went away feeling that this was a really good retro. It felt good to focus on happiness. Happiness is something I’ve been talking a lot about on the This Agile Life podcast, and it felt good to take some action on it. I’ve done “positive-only” retros before, but this one felt even better than that, by specifically targeting happiness and how we can achieve it.

+]]>
+ http://blog.boochtek.com/2015/11/09/happiness-retrospective/feed + 0 +
+
+
diff --git a/public/author/booch/page/2.html b/public/author/booch/page/2.html new file mode 100644 index 0000000..aa5c782 --- /dev/null +++ b/public/author/booch/page/2.html @@ -0,0 +1,766 @@ + + + + + + + + + + + + + + + +Craig Buchek – Page 2 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Impromptu Retrospective

+ + + +
+

I’m surprised that I haven’t gotten this story down in print before. It’s something I’ve mentioned many times — including a few times on the podcast. It’s a great story about the power of retrospectives, and it’s a great story about the power of a blameless post-mortem.

+

I don’t recall all the specifics at this point. It was about 5 years ago. I’d just noticed that Arun had made some sort of mistake. That’s fine, people make mistakes. The thing that was different about his mistake was that I had made the same mistake about a week prior. And Amos had made the same mistake about a week before that.

+

Noticing a pattern of mistakes, Amos and I called an impromptu retrospective. We gathered all the developers into a conference room. We explained the problem that we were running into. At first, Arun was defensive. That’s understandable; he thought we were there to come down on him, to lay blame. But we made it clear that we weren’t focusing on him. We admitted that we had also made the same mistake recently. We weren’t there to lay blame; we were there to figure out how our team could stop making the mistake. It took Arun a few minutes to get over the defensiveness.

+

With the defensiveness out of the way, we could focus on the issue at hand. We were able to figure out the root cause of us all making the mistake. (I don’t know if we played the “5 whys” game, but I’m sure we effectively did something similar.) And with that, we were able to change our process, so that nobody else would make the same mistake again.

+

There are 2 important points to this story. First, you don’t have to wait until a scheduled retrospective to hold a retrospective. This one was impromptu, and it’s the best one we ever had. We saw a problem, addressed it, and found a solution in less than an hour. Had we waited until the end of the week, we would have forgotten some of the details, and wouldn’t have been as effective at solving the problem. Second, when addressing problems, take your ego out of the equation. If you’re in a position of authority, take the blame — but never place blame. Focus on what’s important — solving the problem.

+

And don’t forget the Retrospective Prime Directive:

+

Regardless of what we discover, we understand and truly believe that everyone did the best job they could, given what they knew at the time, their skills and abilities, the resources available, and the situation at hand.

+

 

+
+ + +
+ +
+
+ +

The Problem With Estimates

+ + + +
+
+
+

I’m a big proponent of Agile (mostly XP; I’m mostly anti-Scrum) and I’ve contributed some to the #noestimates “movement”.

+

I don’t really mean that nobody should ever estimate anything. I mean that I’ve never seen useful (fine-grained) estimates anywhere. Here are some of the problems with estimates that I’ve seen frequently:

+
    +
  1. We’re not good at estimating how long things will take. We’re usually optimistic about how quickly we can get things done, and almost always miss thinking about things that will take more time. I’ve never seen a case where a project is completed more quickly than estimated. I’ve only rarely seen fine-grained (story-level) tasks completed more quickly than estimated.
  2. +
  3. Management asks for estimates and then treats them as deadlines. The team then learns to inflate their estimates. Then management learns to reduce the estimates they’re given. Given fudge factors in each direction, the estimate no longer has much reliability. Even if you’re using story points, the point inflation/deflation leads to less consistency and therefore reduced reliability.
  4. +
  5. Estimates that are given are negotiated down, or simply reduced. This leads to the question why you’d ask for an estimate and not take the answer provided. If you’re not going to listen to the answer, why are you asking the question? This is probably the craziest one on the list — given my first point, increasing an estimate would make sense. Reducing the estimates is just magical wishful thinking.
  6. +
  7. Plans change and work is added, but the deadline (presumably based on the estimates) is not changed to correspond with the extra work involved. So again, you’re not actually even using the estimates that were given.
  8. +
  9. Management dictates deadlines arbitrarily, without speaking to the people who will be doing the work. Spending time estimating how long each task will take when the deadline is already set is completely pointless.
  10. +
  11. Almost every deadline is complete bullshit, based on nothing. Often the excuse is that marketing needs to know when something will come out, so that they can let people know about it. Why they need to know the exact release date way in advance, I’ve never been able to figure out. Many people intuitively know that the deadlines are bullshit, and will likely be allowed to slip. The only exception to bullshit deadlines I’ve come across are regulatory deadlines. (I know there are a few other exceptions out there.)
  12. +
  13. Estimation at a fine-grained level isn’t necessary. Many Agile teams estimate using story points, and determine a conversion from story points to time based on previous empirical data. This is fine, except that the time spent estimating the story is wasted time — counting the number of stories almost always gives the same predictive power. Teams tend to get better at breaking up stories over time, so that they’re more consistent in size, so this becomes more likely over time.
  14. +
  15. The ultimate purpose of an estimate is to evaluate whether the proposed work will be profitable, and therefore worth doing. Or to compare the ROI (return on investment) between alternative projects. But to know that, you’ll have to know what value that work will provide. I don’t believe I’ve ever seen that done — at least not at a fine-grained level. Usually by the time you’re asked to estimate, the project has already gotten approval to proceed.
  16. +
+

I’ll note that most of these pit management against the team, instead of working together toward a common cause. Most of the practices also lead to seriously demoralizing the team. And most of the time, the estimates aren’t really even taken into account very much.

+

My advice is to first understand the value of a project before you consider estimating the costs. The estimation at this point will be very rough, so make sure that you have a very wide margin between the expected value and the rough estimate of the cost. If you’re pretty certain of the expected value, I’d probably want to make sure I could still be profitable even if it took 3 or 4 times as long to complete as the rough estimate. And if there’s uncertainty in the expected value, much more.

+

Another way to mitigate the risk of throwing money at something that’s not going to have positive ROI is to reduce the feedback loop. Order the work so that the tasks are ranked in order of value to the customer. (Realistically, you’ll have dependencies of tasks to worry about, and should consider effort involved too.) So work on the most valuable feature first — get that out into production as soon as possible. Once that’s done, you can assess if your ROI is positive or not. Keep iterating in this fashion, working on the features that will provide the most value first. Keep assessing your ROI, and stop when the ROI is no longer worth it, compared to other projects the team could be working on.

+

At a fine-grained level, if you’re using story points, I’d ask you to do the math to see if just counting the stories would be as effective at predicting how much will be done over time as using the story points. If so, you can save the time the team spends on estimating stories. I’d still recommend spending time talking about stories so that everyone has a shared understanding of what needs to be done, and to break stories up into a smaller, more manageable size — with one acceptance criteria per story. Also take a look to see if empirical average cycle time (how long it takes a single story to move from start to finish) might provide you the predictive power just as well as estimates. (I.e. is it bandwidth or latency that really provides the predictive power you’re looking for?)

+

And don’t forget Hofstadter’s Law: It always takes longer than you expect, even when you take into account Hofstadter’s Law.

+
+
+
+
+
+
+ + +
+ +
+
+ +

Website Checklist

+ + + +
+

While creating a web page isn’t too difficult, there are a lot of moving parts involved in creating an effective website. We’ve written up this checklist as a guide for anyone who has decided that they need a website.

+

This list is for a basic static website. A web application will require significantly more effort. We’re working on a separate checklist for web apps.

+

Overview

+
    +
  • Determine your goals
  • +
  • Develop a business plan
  • +
  • Register a domain name
  • +
  • Set up DNS hosting
  • +
  • Set up web hosting
  • +
  • Design and develop the site
  • +
  • Deploy and test the site
  • +
  • Market your site
  • +
  • Set up additional services
  • +
  • Maintenance
  • +
+

Goals

+
    +
  • What do you want to achieve from having the site?
  • +
  • What “call to action” do you want visitors to take?
  • +
  • How will you measure success?
  • +
  • What will be the focus of the site? +
      +
    • Info (“brochure”) for an existing business
    • +
    • Blogging
    • +
    • Sales
    • +
    • Community
    • +
    • Web app
    • +
    • Mobile app
    • +
    +
  • +
+

Business Plan

+
    +
  • Who is your target audience?
  • +
  • Marketing +
      +
    • How will you get them to come to your site?
    • +
    • How will you get them to buy something?
    • +
    +
  • +
  • Who is your competition? +
      +
    • How do you compare to them?
    • +
    • How do you differentiate from them?
    • +
    • What is your niche?
    • +
    • What makes your site better than theirs?
    • +
    +
  • +
  • How will you make money? +
      +
    • Bringing people into brick and mortar business
    • +
    • Advertising
    • +
    • Periodic billing/subscriptions
    • +
    • Selling goods/services
    • +
    • Get bought out
    • +
    +
  • +
  • Pricing +
      +
    • Aim high — it’s easier to lower prices than raise them
    • +
    • Tiered pricing often makes sense; most people pick the middle tier
    • +
    +
  • +
  • What will it take to have a positive ROI?
  • +
+

Domain Name

+

You’ll probably want your own domain name.

+
    +
  • Think of a name +
      +
    • Stick with a .com name if possible; don’t use .biz
    • +
    • Some other top-level domains come into fashion on occasion +
        +
      • .io is pretty popular right now
      • +
      +
    • +
    +
  • +
  • Check availability of the name
  • +
  • Keep checking names, until you find a good name that is available
  • +
  • Register the name with a respectable registrar +
      +
    • DNS Registrars run from $10-$35/yr
    • +
    • DO NOT allow your web host provider to own/control your name
    • +
    • You may want to grab the .net, .org, and other versions of the same name
    • +
    • Multiple-year registration is cheaper, but it’s easier to forget how to renew it
    • +
    +
  • +
  • Your registrar will likely point your domain to an “under construction” page initially
  • +
  • DO NOT LOSE YOUR NAME! +
      +
    • Spammers and pornographers will take it over if your registration lapses
    • +
    • Make sure your contact info (especially email address) is up-to-date
    • +
    +
  • +
  • Beware scams (usually by US mail) trying to get you to renew with a different registrar
  • +
  • Note that the email address you register with will get spammed +
      +
    • Some registrars provide some protection for a fee
    • +
    +
  • +
+

DNS Hosting

+

You’ll need to have someone take care of the servers that tell the world what server addresses your domain name corresponds to.

+
    +
  • Find a DNS hosting provider +
      +
    • We like DNSimple; they also do domain registration
    • +
    +
  • +
  • Provide the name servers to your DNS registrar
  • +
+

Web Hosting

+

You’ll need servers to host your web site on. There are a lot of options available, from virtual hosts (a Linux server where you control everything) to application-specific services.

+
    +
  • Talk to your developer or designer first! +
      +
    • Web host can significantly restrain the development environment
    • +
    +
  • +
  • Cost +
      +
    • $10 to $500 / month is typical
    • +
    +
  • +
  • Bandwidth +
      +
    • Number of users × how often they use it × average “page” size
    • +
    • What happens if you go over?
    • +
    +
  • +
  • Up-time +
      +
    • What kind of down-time can the site sustain?
    • +
    • Higher guaranteed uptime costs more
    • +
    • What if the specified uptime is not met?
    • +
    +
  • +
  • Development environment +
      +
    • What programming languages are installed?
    • +
    • What databases are installed?
    • +
    • What libraries are installed?
    • +
    • What if other libraries are required?
    • +
    +
  • +
  • Shared/dedicated/virtual hosting +
      +
    • Shared means others are using the same machine, with security implications
    • +
    • Dedicated is expensive, but you “own” the whole machine
    • +
    • Virtual is somewhere in between +
        +
      • You have a “slice” of a machine dedicated to your site
      • +
      +
    • +
    +
  • +
  • How responsive is the host to problems and requests?
  • +
  • Backups +
      +
    • What do they back up?
    • +
    • How often do they back up?
    • +
    • How can files be restored?
    • +
    +
  • +
+

Design and Development

+

Designing and developing the site can vary from picking an existing template and adding content, to developing a full web application.

+
    +
  • Cost ($30 – $300 / hr)
  • +
  • Project management +
      +
    • Story/task management
    • +
    +
  • +
  • Revision control +
      +
    • Ensures changes can be rolled back quickly
    • +
    +
  • +
  • Functionality +
      +
    • What does the site need to do?
    • +
    +
  • +
  • Usability +
      +
    • How easy is it to use each page?
    • +
    • Is it easy to navigate the site to find what you’re looking for?
    • +
    • Check for broken links
    • +
    • Check for ADA/508 compliance
    • +
    • Spell checking and grammar checking
    • +
    +
  • +
+

Deploying and Testing

+
    +
  • Can updates be deployed quickly? +
      +
    • Deploy early and often, so it’s not such a big deal, and it becomes routine
    • +
    +
  • +
  • Consider a staging and/or beta site +
      +
    • Test everything thoroughly on staging site before deploying to production
    • +
    • Staging site should (mostly) use the same code as production
    • +
    • Staging/test site should not process credit cards, etc.
    • +
    +
  • +
  • Automated testing +
      +
    • Prevents regressions
    • +
    +
  • +
  • Exploratory testing +
      +
    • See how things work
    • +
    • See if you can break things
    • +
    +
  • +
  • Security testing +
      +
    • Penetration testing
    • +
    +
  • +
  • Performance +
      +
    • Load testing
    • +
    +
  • +
  • Beta testers
  • +
+

Marketing

+
    +
  • Search engine “optimization” (SEO) +
      +
    • Good design, good URLs, and well-written HTML should cover most of this
    • +
    • Submit site to search engines
    • +
    • Use robots.txt and site maps
    • +
    +
  • +
  • Directories
  • +
  • PR sites
  • +
  • Targeted groups
  • +
  • DO NOT send out spam
  • +
+

Additional Services

+

What other services do you need, besides just the web site?

+
    +
  • Email
  • +
  • Blog
  • +
  • Wiki
  • +
  • File storage
  • +
  • Forums
  • +
  • Authentication
  • +
  • Customer interaction +
      +
    • CRM
    • +
    • Feedback
    • +
    • Bug tracking
    • +
    • Customer Q&A (StackExchange)
    • +
    • Follow-up emails to customers to offer assistance
    • +
    +
  • +
+

Maintenance

+

Over the lifetime of the site, you’ll likely pay more in maintenance costs than the upfront costs.

+
    +
  • Responding to user emails
  • +
  • Requests for info
  • +
  • Feedback about the site
  • +
  • Password resets?
  • +
  • Tracking bug reports and feature requests
  • +
  • Site improvements
  • +
  • Additional content
  • +
  • Moderation of user content +
      +
    • Spam removal
    • +
    +
  • +
  • Log analysis +
      +
    • Google Analytics
    • +
    +
  • +
  • Assessing advertising effectiveness
  • +
  • Analysis of revenues/profitability
  • +
  • Upgrades +
      +
    • New/improved functionality
    • +
    • Bug fixes
    • +
    • Upgraded infrastructure
    • +
    +
  • +
  • Down-time +
      +
    • Web host
    • +
    • Upgrades
    • +
    • Accidents
    • +
    • Bugs
    • +
    +
  • +
  • Backups +
      +
    • Testing restoring from backups
    • +
    +
  • +
  • Payments for services +
      +
    • Domain name registration – DO NOT LOSE YOUR NAME!
    • +
    • Web hosting
    • +
    • Marketing/advertising
    • +
    +
  • +
+

 

+
+ + +
+ +
+
+ +

Potential F5 Vulnerability

+ + + +
+

It all started with an email about a WebInspect report. It listed a buffer overflow, which we had marked as a false positive. I read the WebInspect report carefully, and found a note at the bottom that said you could test manually to confirm whether it was a false positive or not. Unfortunately, the manual test listed had a few problems. First, it jammed the lines together, without the proper line-breaks. Second, it assumed the site was using HTTP, not HTTPS, so used telnet. Third, it was testing against a page that didn’t exist, giving a 404. Keeping those in mind, I tried the manual test using the openssl s_client command:

+
openssl s_client -quiet -connect mysite.example.com:443
+POST /login HTTP/1.1
+Host: mysite.example.com
+Transfer-Encoding: chunked
+
+c00000000
+

The test terminated immediately. The report said that this meant that the server was vulnerable to a buffer overflow and arbitrary code execution.

+

At first, we thought this was caused by Apache or Tomcat, or possibly the application code. But the reported vulnerability was an Apache CVE from 2002 (CVE-2002-0392), fixed by vendors long ago. After a while, we realized that if we hit the servers directly, we did not get the indication of a vulnerability. If we hit the site through the F5 VIP, we saw the immediate termination of the connection. The issue is with handling of HTTP chunked encoding. Nginx had a similar issue in 2013 (CVE-2013-2028).

+

So we turned our attention to the F5 load balancers. We were able to confirm that other sites using F5 load balancers were exhibiting the same behavior. We also confirmed that WebInspect run against the servers directly did not show the issue (even as a false positive). We reported the issue to F5, and they are looking into it.

+

I’m disclosing this publicly now for a few reasons. First, I’m not a security researcher, and almost nobody follows my blog — especially people looking for security issues that could be exploited. Second, I’ve not developed a payload, so I have no idea whether this is exploitable. But at this point, it’s merely a potential vulnerability. I’m not sure I’ll even spend the time to research and create a payload to prove the vulnerability. If I do, I’ll be more careful with the disclosure.

+

 

+
+ + +
+ +
+
+ +

Not Quite Callbacks

+ + + +
+

I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+
+ + +
+ +
+
+ +

Architectural Thoughts

+ + + +
+

I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+
+ + +
+ +
+
+ +

From Agile To Happiness

+ + + +
+

The Agile Manifesto was written in 2001, as a way to explain the common values amongst several “light-weight” software development methodologies that had come about. The term “Agile” was chosen as a shorthand for those commonalities.

+

Once “Agile” started to show success, we started to see many people use the term to market their products and services, whether or not they really believed in the values and principles of the Agile Manifesto. It’s gotten to the point where some of us don’t see much value in using the term “Agile” any more. Even some of those involved in creating the manifesto have suggested new terms. Dave Thomas suggests “Agility” and Andy Hunt has started working on something called GROWS. Personally, I’m considering going back to the term “Extreme Programming”, even though I’ve incorporated ideas from other Agile methodologies.

+

It recently occurred to me that Agile, when done “right”, is closely aligned with the happiness of the team members. This is really interesting, because it aligns the interests of the employees and the business — a win-win situation. My next thought was that maybe the next step after “Agile” will be a focus on happiness and motivation.

+

I’ve recently been thinking about personal motivation lately, in the context of team practices. According to Daniel Pink’s book Drive, people are motivated by autonomy, mastery, and purpose. I personally add a fourth that can sometimes trump the other three: identity. And of course, happiness can also be motivating — both in the attempt to achieve happiness and in just being happy. (I suspect that happiness is more of a parallel to motivation than a cause though.)

+

There are a couple different ways that happiness can be achieved at work. The traditional way is for work to be a means to an end. In this view, the purpose of your job is to provide the money to live your life (outside of work) the way that you want to live it. There’s nothing wrong with this way of thinking. But for the lucky few, we can work on something that makes us happy in and of itself. That’s generally done by finding a job doing something that we enjoy.

+

But perhaps that’s thinking about things the wrong way. For one, plenty of people who have gone that route are still unhappy at work. I think a lot of that has to do more with the context surrounding the work than the work itself. Maybe you’ve got a lousy manager. Maybe you don’t like the people you work with. Maybe the tools you have to work with are bad. Maybe the processes add a lot of unnecessary tedium.

+

So maybe we need to find ways to be happier at work. Most of the Agile practices seem to make team members happy. For example, replacing a light-weight process for a heavier process always makes me happy. And I typically leave retrospectives in a good mood. So that’s a good starting point. But we should see if we can take the idea further. If we take employee happiness as a core value, where can we go? What kind of practices would we want to add? Please share any ideas in the comments below.

+
+ + +
+ +
+
+ +

When Should We Do TDD?

+ + + +
+

On a recent episode (78) of This Agile Life, my fellow hosts talked about when to do Test-Driven Development (TDD). They all said that you should always do TDD — at least for anything that will go into production; there’s an exception for experimenting or “spiking”.

+

I wasn’t on that episode, but later commented on the topic. (Episode 83 — which wasn’t really all that great.) My take was slightly different. I said that you should do TDD only when the benefits outweigh the costs. Unfortunately, we usually greatly underestimate the benefits. And the costs often seem high at first, because it takes some time to get good at writing automated tests. Not to mention that both the costs and the benefits are usually hard to measure.

+

What is the cost of writing automated tests? I’ve asked this question before and recorded the answers (inasmuch as we have them) in a previous blog entry. TDD costs us about 10-30% in short-term productivity. The benefit that they found was a reduction in bugs by 30-90%, and a decrease in code complexity by about 30%.

+

But what about when the costs are higher than the normal 10 to 30 percent? One good example of this is when there’s no test framework for the situation you’re testing. This might be a new language or framework that you’re working with. More likely it’s a complex API that you have to mock out. So that could increase the cost of automated testing so as to outweigh the benefits — especially on a short project. I could imagine situations where the cost of writing the mocks could eat up more than the project itself.

+

Another case where we might consider skipping testing is when we’re more concerned about time to market than quality. This is almost always a mistake. Your code will almost always last longer than you expect. (Remember Y2K?) And if the code lasts longer than you expect, that means you’ll have to deal with bugs that whole time. But we have to work with the information we have at the time we make our decisions. And sometimes that might tell us that time to market is more important than anything.

+

One final case I can imagine is when a true expert is coding something that they’re very familiar with. I could picture someone like Uncle Bob writing code (in a language that he’s familiar with) without tests just as effectively as I could write code with tests.

+

But these situations should not be common; they’re edge cases. In almost all real-world cases, TDD is the right thing to do. Don’t forget, TDD is also a design discipline — it helps you design a better API. So keep doing TDD. But as with any practice, don’t do it blindly without considering why we’re doing it. Make sure you understand the costs, and whether the benefits outweigh the costs.

+
+ + +
+ +
+
+ +

Good Enough

+ + + +
+

I ran into some former colleagues recently, from a company where I had worked to help transform the team to be more Agile. They’ve gone through some reorganization and management changes recently. One of the guys said that their team culture has helped them maintain quality in the face of those changes. This struck me as odd, since I had considered the work I had done there as somewhat of a disappointment. While I felt I had made a small dent, I didn’t feel like I’d made a true Agile transformation. Much of what I had taught didn’t seem to “stick”.

+

Later that night I thought about why my opinion of the team and his opinion were so different. There are a lot of reasons why an Agile “transformation” could fail. It could be due to lack of support from management. Or even worse, not getting the team members to buy into the ideas — usually due to fear of change. But while those had some effect in this case, they weren’t really the main issues. Now that I’ve had some time and some distance from that team, I’ve gained some clarity on what the real issues were.

+

I think the reason that I think this transformation was a failure was due to the continued pace of change that true Agile requires. Basically the team experienced “change fatigue”, where everything keeps changing and you feel like you can never catch your breath. But the more interesting thing is how our perceptions differed. He seemed to think that the transformation was a success — they’re doing things better than they used to. I view it as more of a failure, because they basically stopped improving — at least at the pace that I was hoping for.

+

I think this difference in mindset is pretty fundamental. My view of Agile involves continuous improvement — always inspecting and adapting our processes. I suppose that means that my preferred flavor of Agile is “Kaizen“. But other people don’t see improvement and change as a constant — they see each change as a means to an end. I’m starting to realize that neither viewpoint is objectively correct. Maybe they’re both fine viewpoints to have. Maybe I shouldn’t be so hard on myself and view that engagement as a failure, especially if my teammates view it as a success. Maybe perfection is the enemy of the good. And maybe I need to learn to be happy with “good enough”.

+

 

+
+ + +
+ +
+
+ +

The Power of 1%

+ + + +
+

I frequently participate in a podcast called This Agile Life. Recently, a listener asked how much time Agile teams should spend on self improvement. I said 10% to 25%, leaning towards 15% to 20% for most teams. That comes to at least one hour per day, and maybe even more than one day per week.

+

I’m including personal self improvement and team retrospectives in this self-improvement time. This can be as simple as configuring your IDE to make you more efficient, learning to use a tool better, or following up on action items from a retro.

+

That may seem like a lot of time “wasted”. But I think I can justify the cost of all that time.

+

The purpose of spending time on team or self-improvement — the whole point — is to increase our performance and our efficiency. How much improvement can we expect? Can we improve by 1% each week? That doesn’t sound too unreasonable. I think that’s an achievable goal for almost any team, at least on average.

+

Spending 20% of your time to gain 1% doesn’t seem like it’s worth it — until you consider the long term. With compound interest, you’ll be 67% more efficient by the end of a year.1 At that point, you’ll be able to get things done in 59% of the time — saving 41% of the time required at the beginning of the year.2 The following years will show even more progress, as compared to when you started. If 10x programmers exist, continuous improvement is apparently the way to get there.

+

So there’s a pretty good return on investment, even with a small amount of improvement each week. You’ll be significantly more efficient.

+

But efficiency isn’t really what you should aim for. You should aim for effectiveness. You can be efficient in creating the wrong thing. Part of improving should be ensuring that you’re not just building things right, but that you’re building the right things. Build what the customer really needs. Find ways to ask the right questions.

+

Most importantly, find ways to keep improving. It would be a waste of time not to.

+

1: (1.01 ^ 52) – 1
+2: (0.99 ^ 52)

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/author/booch/page/4.html b/public/author/booch/page/4.html new file mode 100644 index 0000000..77dada4 --- /dev/null +++ b/public/author/booch/page/4.html @@ -0,0 +1,781 @@ + + + + + + + + + + + + + + + +Craig Buchek – Page 4 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Includable ActiveRecord

+ + + +
+

I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+
+ + +
+ +
+
+ +

Empathy

+ + + +
+

I facilitated our team retrospective this morning. I felt like we made a little forward progress, but not as much as I would have liked. But it really brought one thing to the forefront of my thoughts today — empathy gained through communication.

+

We have a pretty large team by Agile standards — we had 20 people in our retro: 16 developers, 3 QA folks, and 1 manager. Out of those, only about 5 or 6 speak up regularly. I recently sent out a survey to the team, trying to get feedback on how we could improve our retros. A couple of the questions tried to get a feel for why people aren’t speaking up more. Only about half the people responded, and the answers didn’t really answer my question as well as I had hoped.

+

So on Amos‘s suggestion, we did the Safety Check exercise. We got a good set of answers to why people don’t feel safe. About half of the answers were about the fear of looking stupid in front of other people. About half of those mentioned the manager — they’re worried they might get in trouble for what they say. We talked some about fear and how it’s more often than not misplaced. And that the worst consequences are usually not as bad as you might think. But then we got to the crux of the problem — there’s not enough trust amongst the team, and especially between the team members and the manager.

+

About half of our team is new (within the past 6 months) — including the manager. While the developers have made some good progress building trust amongst each other, we haven’t had as much time with the manager to build trust between him and the rest of the team. So the lack of trust isn’t at all surprising.

+

Honestly, I already knew we had trust issues, and wanted to address them, but needed a way to lead the team to that same realization. With this exercise driving out the issue, we were then able to have a conversation about trust. The conversation was pretty good. We got more voices to contribute than probably any other topic we’d previously covered. (I was disappointed that the manager kept quiet though. I later found that he was trying to mitigate people’s fears by keeping quiet, but I urged him to contribute more in the future.)

+

But one point really stood out in my mind — a point of view that I hadn’t previously thought much about. Lauren, one of our QA analysts, pointed out that most human communication is non-verbal. We give tons of cues via body language, facial expressions, eye contact, tone of voice, even posture. I don’t recall if Lauren said it explicitly, but she pointed out that these cues build empathy between the speakers. She encouraged us to use more voice chat and video chat, as opposed to IRC text chat, because it would create more empathy between the people communicating, which would lead to more trust.

+

I spent most of the rest of the day talking to people on the phone or via Google Hangouts voice. And every single time, I consciously noticed that I was gaining empathy for the person I was speaking with. I assume (and hope) that that’s working both ways. I suppose that it’s always been happening, but I never really noticed it.

+

I’ve heard a lot of talk about empathy among Agile practitioners lately. It’s been mentioned on the Ruby Rogues podcast, and Angela Harms has been preaching it for years. I already understood how important it is. But until today, I didn’t really feel it happening.

+

So if you’re looking to build trust with someone, spend some time talking with them. Preferably in person, but if that’s not possible, seriously consider video or voice modes of communication, instead of sending an email or an instant message.

+

 

+
+ + +
+ +
+
+ +

What I Want in a Blog Engine

+ + + +
+

I’m considering moving away from WordPress, for a couple reasons:

+
    +
  • Security. There have been several vulnerabilities over the past few years, and I’ve never really had a high level of confidence that it’s secure. In addition, I now find the whole PHP model — every web app running as a single user, instead of leveraging UNIX permissions — to be broken.
  • +
  • Speed. I started to realize a couple years ago that a blog engine should generate static pages. The static pages should be updated whenever new content is added. There’s really no reason to re-generate the page every time someone wants to read it. At the very least, Server-Side Includes (SSI) or Edge-Side Includes (ESI) should be used.
  • +
+

Now that I’m starting to actually blog consistently, it makes sense to change. But before I move to something different, I want to be sure that I find all the features that I need. This post is my thought process about what I want.

+

Requirements

+
    +
  • Self hosted. I like to run my own servers. Partly so I can keep full control, and partly because I enjoy doing some system admin stuff (as long as it doesn’t take too much time).
  • +
  • Generation of static pages. Almost everything should be a static page by the time a user requests a page. Obviously, submitting comments would hit a dynamic page, and comment approval would likely be done through a dynamic part of the site. But publishing a blog post or comments should generate static files to be served by Apache or nginx.
  • +
  • Categories (or maybe tags). I want readers to be able to find other things that I’ve written that might interest them. A list of categories in a sidebar would help them find what interests them.
  • +
  • Site map. Especially an XML sitemap, so Google can index better. I also like the idea of readers being able to see everything in one place.
  • +
  • Archives. This is another way of letting readers see everything I’ve written, organized by publication date.
  • +
  • RSS and/or Atom. I want readers to be able to subscribe to my blog.
  • +
  • Media. I don’t think I necessarily need a media manager, just a simple way to add pictures to blog posts.
  • +
  • Comments. I want to allow comments, but I don’t want to use anything like Disqus. I want the comments to be in my own database, so they won’t disappear. I also dislike requiring JavaScript just to see comments. And I’ve worked at places where Disqus wouldn’t work, due to web proxy servers. Comments should be moderated by the site admin, editor, or author. Readers should not have to log in to post a comment, but it would be nice to allow users to log into Twitter or some other social network to provide their info.
  • +
  • Pingbacks and Tweetbacks. I’d like to be able to see who’s referring to my articles.
  • +
+

Nice to Have

+
    +
  • Free. I almost consider this to be a requirement, but I’d be willing to entertain a 1-time payment for something that meets my needs really well. I need to try to counteract my DIY-at-all-non-monetary-costs mentality.
  • +
  • Open Source. I think Open Source is likely to provide a more useful product, especially in this space. An Open Source option is more likely to improve over time.
  • +
  • Not PHP. I’ve got several problems with PHP. First, the whole PHP model of running in the same context as Apache (or nginx) breaks UNIX conventions. A security issue in a PHP program exposes more than just the program itself — it exposes Apache and every other PHP program. Under the UNIX model, each program runs in a different user context, isolating bugs to that program only. PHP is also a “hacky” language. PHP programmers tend to not be as rigorous (in many ways), leading to more vulnerabilities. Finally, the PHP language itself has had several misfeatures that have lead to security issues — much more than Ruby or Java.
  • +
  • Not MySQL. I’d like to be able to remove MySQL from my servers. PostgreSQL would be much preferred. Storing everything in git would be fine too — maybe even preferred. I’d even be fine with MongoDB or some other simple NoSQL database.
  • +
  • Admin console. I can’t imagine approving comments without this, but I’m not closed to that possibility.
  • +
  • Edit history. I want to be able to update posts, but see what the previous versions looked like. (Not sure if I care whether the public can or cannot see the various revisions.)
  • +
  • WYSIWYG. I’d be willing to settle for Markdown. In fact, I’d prefer (extended) Markdown as the canonical form behind the WYSIWYG.
  • +
  • Code highlighting. A lot of my blog posts (will) include code in various languages. I’d like the blogging software to be able to handle that well. Syntax highlighting would be great. Any other related features would also be nice.
  • +
  • Through-the-web editing. I’d like to be able to edit a page from any device from anywhere that has Internet access.
  • +
  • Reader-submitted edits. This is probably going to be the hardest thing to find, but I’d like a really easy way for readers to let me know of typos and such by simply submitting a fix for them. The author or editor of the post would have to approve the edits, obviously.
  • +
  • Import from WordPress. I don’t have a ton of content in WordPress, or else this would probably be a more firm requirement.
  • +
  • Community. Community helps software thrive, adding improvements and ensuring security.
  • +
  • Plugins. It would be nice for the blog engine to be extensible.
  • +
  • Unpublished drafts and previews. I’d really like to be able to see by blog posts before I release them to the world.
  • +
  • CMS abilities. I’d really like the ability to edit non-blog web pages the same way as blog entries.
  • +
  • Themes. It would be nice to have decently looking themes, so I could have other people help me make the site look nice.
  • +
  • Posting my tweets in a sidebar area. I’d be OK with this being done in client-side JavaScript. I’d also be OK with periodic polling of my Twitter feed, and updating the static content when appropriate.
  • +
+

Candidates

+

I’ve been playing with Jekyll lately, using it to generate a simple static site that can be updated easily. It’s pretty nice, and pretty easy to use. Something based on Jekyll would work well. The trickiest part will be figuring out how to manage comments. Or maybe I’ll just have to learn to live more minimally.

+

Octopress is the obvious candidate. It fits most of my needs and wants. Most likely I’ll write my own commenting engine and add it to my Octopress template.

+

The only other candidate that I’ve found is Ghost. It’s written in JavaScript and runs on Node. I don’t find Node to be that compelling, but the templates look great, and it’s designed for coding blogs.

+

I’ll probably take a quick look at Droplets, too. It’s written in PHP, but seems to fit my other requirements.

+

Unfortunately, that seems to be all there is in this space. Maybe I need to investigate more. Or maybe Octopress just has the static blog generation marked cornered. Either way, I’ll probably change my blogging software in the next month or two. Wish me luck!

+

 

+
+ + +
+ +
+
+ +

Testing Rails Validators

+ + + +
+

It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+
+ + +
+ +
+
+ +

Introspective

+ + + +
+

Today I was informed that I’ve been impeding progress on our team.
+This was somewhat shocking, since I feel like I’m always pushing
+forward to make our team and myself better.

+

Like most anyone, my initial reaction to criticism was defensiveness.
+I don’t handle criticism well. (You might find that hard to believe,
+after reading the rest of this post. Maybe it’s just the confrontation
+part I don’t handle well.) Thankfully the blow was softened somewhat,
+because the person providing the criticism didn’t tell me directly —
+they told Amos, a trusted colleague of mine. Amos then passed it on to
+me. I’m grateful for that — this is the best way for me to have received
+that criticism.

+

What I did next is the astonishing thing. I swallowed my pride for a
+minute. Not an easy thing for me to do, for all the usual reasons, and
+more. I decided to address the problem head on. If someone was telling
+someone else that I was causing some sort of a problem, then even that
+perception was a problem that I needed to address. So I decided to hold
+a retrospective for myself. Retrospectives are the way Agile teams address
+problems and improve how they work. If it would work for a team, I figured
+it should work for an individual.

+

I’d held retrospectives by myself before. Before I even knew about
+retrospectives, I’d taken some time to assess how I had done things
+on a couple of projects I had worked on as an independent contractor. But
+those were about technical decisions I had made and processes I had
+followed. This one would be different. This one was about a personal
+shortcoming, an action of mine that had caused some harm. This one
+involved feelings and emotions.

+

So I asked Amos to facilitate my one-person retro. We’d both done a lot
+of retrospectives, but neither one of us had done anything like this before.
+We had a bit of trouble getting started. We didn’t have any details
+about why the anonymous person felt the way he felt. So it was hard
+to address something that nebulous. So I asked Amos if he could follow
+up with the person. Luckily, the person was online and able to respond
+in real time.

+

So we talked things through some. We went on some tangents. We made some
+progress — things that I could write down as take-aways and action items.
+We got off-topic again, and then came back to it. Then Amos decided to
+play The Why Game. This worked amazingly well. It helped me to find the
+root reasons why I was holding back change. Basically I’m afraid of
+failure. Going deeper, we looked into why I’m afraid of failure. This
+line of inquiry was particularly effective. Hopefully it’ll be enough for
+me to stop being an impediment to change. Here are my notes / take-aways:

+
    +
  • Be careful playing devil’s advocate in public. +
      +
    • People will think I’m taking that stance.
    • +
    +
  • +
  • Don’t always take the boss’s side in public.
  • +
  • Don’t be the person to allow the team to take the easy path.
  • +
  • Why do I think that managers won’t let us try a 2nd time?
  • +
  • Why do I think we might fail the first time? +
      +
    • I want to put us in a better position to succeed.
    • +
    +
  • +
  • I’m being too conservative.
  • +
  • Be willing to fail. Don’t fail to try.
  • +
  • Don’t say we shouldn’t try because I think other people don’t want to try. +
      +
    • That is just self-perpetuating.
    • +
    +
  • +
  • I’m afraid of failing because I haven’t done it much. +
      +
    • Because I’m good at learning from other people’s failures.
    • +
    • But this is holding me back and making me cautious. +
        +
      • Despite the fact that I try to not be cautious.
      • +
      +
    • +
    • So I need to work to counter-act those fears.
    • +
    +
  • +
+

At first, I thought that I’d never heard of anyone doing anything like
+this. But then I recalled that I’d heard about somewhere that everyone
+does this, with their whole team. That would be hard. I’d have to
+trust everyone in the room.

+

But I would recommend this for anyone that can pull it off. It’s hard,
+but it has a large payoff. Just like a team retrospective, it feels good
+making this kind of breakthrough, and knowing that just spending that
+little amount of time has made me a better person. (Mine took about an hour.)
+I think the hardest part is finding someone (or a group of people) you
+trust to be your facilitator.

+

I’ve decided to call this thing an introspective. A retrospective is
+about looking back. This is about looking inward. I’d be interested to
+find out who is doing this kind of thing. Does it have a commonly accepted
+name? How does it work? What techniques work best? If you’ve got any answers or ideas, please comment below, or tweet me @CraigBuchek.

+

So thank you to that anonymous person. Your way of addressing this was
+probably more effective than you could have imagined that it might be.
+I hope that this exercise will help me make the changes required, to make
+me a better person, and help the team become the best that we can be.

+

Amos has written up his thoughts on the introspective.

+
+ + +
+ +
+
+ +

What’s Your Open Source Resolution?

+ + + +
+

I’ve been using GNU/Linux since 1994, starting with a Slackware 2.2 CD-ROM I ordered from the back of a magazine. I’ve been heavily involved in my local GNU/Linux community since 1999, and I give frequent presentations at various groups. I’ve made some small contributions to several Open Source projects.

+

But I don’t feel like I’ve done enough to contribute to the wider community. So this year, I’m going to resolve to step up my contributions, and do more.

+

Change my role in the LUG

+

I was lucky enough this past year to find someone interested in helping to run the St. Louis GNU/Linux Users Group (STLLUG). I’ve been running the group so long that I’ve somewhat stagnated. It’s great to find someone who will bring a fresh energy and new ideas. Thanks, Andrew!

+

Mostly freed from the more general administrative tasks (mostly planning meeting topics and speakers), I’m hoping to find the time to work on a few projects. First, I want to overhaul the web site. It needs a new look, as well as an easier way to update it. I’m planning to implement some sort of CMS — possibly WordPress or Octopress, or at the very least, a git repo to manage updates.

+

Blog regularly

+

I installed WordPress a long time ago, but I haven’t published many posts. I’ve got a few drafts in various stages. So this year I’m going to actually get the blog going, and hopefully post about once a week. I’ll be sharing things I learn on the Internet, as well as some of the things I learn from my experiences with various technologies and techniques.

+

Contribute more significant patches

+

There are a few items I’ve been meaning to work on and contribute. I’d like to finally get around to them:

+
    +
  • Make the GNU sort command be able to (easily) sort a list of IP addresses, or semantic version numbers.
  • +
  • Add MySQL-style commands to PostgreSQL: SHOW DATABASES; SHOW TABLES; DESCRIBE table. (I’m not sure how welcome these additions might be, but I think they’d make transitioning from MySQL easier for people.)
  • +
+

Publish and maintain several Ruby gems

+

I’ve been working on a few Ruby gems. I’d like to get them to a state where they’re useful to more people. These include:

+
    +
  • A way to simplify standard CRUD controller actions
  • +
  • A way to declare list views in a manner similar to SimpleForm or Formtastic
  • +
  • A way to declare ActiveRecord attributes in models using Virtus
  • +
  • A higher-level interface to ROM (Ruby Object Mapper)
  • +
  • A similar high-level way to define Perpetuity attributes in models using Virtus
  • +
  • My Rails template, making it much easier to start a Rails app
  • +
+

Build some apps

+

I enjoyed building a quick Rails app during last year’s Rails Rumble, even if it doesn’t turn out to be useful or make any money. I’d like to create a few small apps again this year, either in Rails or Apache Cordova. I’ve got some ideas — we’ll see how if they turn into anything useful or worth selling.

+

Podcast more often

+

Last year, I started joining the This Agile Life podcast. I enjoy it. I’m hoping to continue with that, and possibly doing some podcasting about Ruby and Rails.

+

Present at a conference

+

I’m going to attempt to give a presentation at a conference. I often give presentations at local groups, but haven’t addressed a larger audience. I’ve got a few ideas rattling around, and will see if I can turn them into presentations worthy of a larger conference.

+

What’s your Open Source resolution?

+
+ + +
+ +
+
+ +

My Thoughts on Python vs. Ruby

+ + + +
+

I’ve been using Python at work for the past few months.  I learned Python back in the early 2000s, but never used it for any large projects.  I learned Ruby in late 2005, and it quickly became my language of choice for most cases.

+

So while I still prefer Ruby, and will likely use Ruby more in the future than Python, I wanted to assess the strengths and weaknesses of Python in relation to Ruby.  Perhaps some of the lessons could be applied when writing Ruby, and it could help to decide when to use each.  Also, I’m interested in programming language design, and wanted to document pros and cons in that light.

+

Where Python Sucks (As Compared to Ruby)

+
    +
  • Have to explicitly include self in EVERY method declaration. +
      +
    • Including @classmethod declarations (although people usually use the name cls instead of self there).
    • +
    • Except @staticmethod declarations.
    • +
    +
  • +
  • Have to use self everywhere to reference an object’s own attributes.
  • +
  • Inconsistency of things like the len() function, when everything else is a method.
  • +
  • Inconsistency of having some built-in classes with lower-case names. +
      +
    • And they’re not whole words, so you have to memorize them: list, dict, str, int.
    • +
    • Even more bizarre is dict vs. OrderedDict.
    • +
    +
  • +
  • I’m not a fan of True and False being uppercase. +
      +
    • I’m a bit less concerned about None, for some reason.
    • +
    • If they’re going to be uppercase, they might as well be all caps, in my opinion.
    • +
    +
  • +
  • Inconsistency of camelCase versus snake_case in some modules.
  • +
  • Claims to prefer explicit over implicit, yet does not use new to create an object. +
      +
    • It is nicely concise, but makes it look too much like a regular function call.
    • +
    +
  • +
  • The implementation of lambdas is too limited. +
      +
    • Makes using map function not very useful.
    • +
    +
  • +
  • Class methods are less than ideal to implement. +
      +
    • Probably better to use functions within a module instead, in many cases.
    • +
    +
  • +
  • Lack of good functional programming tools makes it harder to manipulate lists. +
      +
    • Have to often resort to creating an initial list and adding to it in a for loop.
    • +
    +
  • +
  • I don’t understand why r’regex_string’ doesn’t just create an actual regular expression object.
  • +
  • I miss Ruby’s method-call-or-property-getter syntax. +
      +
    • Nice that I can get it by adding @property to method definitions, but that’s a bit messy.
    • +
    +
  • +
  • I don’t understand why lists don’t have a join() method; it seems backwards to call join on the string used to connect the list elements.
  • +
  • I miss unless; seems like with all the keywords, Python would have added that.
  • +
  • I really miss ||= to memoize. +
      +
    • The distinction between unset variables and variables set to None makes it hard.
    • +
    • I also miss +=.
    • +
    +
  • +
  • I really miss statement modifiers (if or unless at the end of the statement/expression instead of the beginning).
  • +
  • I miss being able to assign to a compound statement (x = if True: 1; else: 2). +
      +
    • While Python does allow this simple case, it does not allow more complex cases.
    • +
    +
  • +
  • The way modules, files, and classes work, I either have a lot of classes in one file, or have to come up with more module names in order to split classes into different files.
  • +
  • The preference for bare functions within modules over class methods often leads to functions outside of classes, when they’d make more sense more closely associated with the class.
  • +
  • I don’t quite understand the necessity for pass. +
      +
    • Seems like allowing 0 lines of indented code would suffice instead.
    • +
    +
  • +
  • I miss implicit return. +
      +
    • Explicit return looks a bit nicer, but is less concise, and I’ve gotten out of the habit.
    • +
    +
  • +
  • Mixins turn out to be more useful than multiple inheritance.
  • +
  • I miss string interpolation.
  • +
  • I really miss Array#first and Array#last.
  • +
  • I really don’t like that 0 is falsey. +
      +
    • I could live with empty lists and dicts being false, but not 0.0 and 0.
    • +
    +
  • +
  • Comparing a string to a regular expression seems harder than it should be.
  • +
  • Converting to a Boolean is not as easy a Ruby’s !! syntax. +
      +
    • OK, bool(expression) isn’t so bad, I guess.
    • +
    +
  • +
+

Where Python Rocks

+
    +
  • Indentation removes the need for end everywhere.
  • +
  • Import statements are nice. +
      +
    • Can import everything, or just a few things.
    • +
    +
  • +
  • Annotations are nice for aspect-oriented modification of methods. +
      +
    • Does it make it hard to debug though, like alias_method_chain in Ruby?
    • +
    • It’s kind of tricky to write them though, due to doubly-nested function definitions.
    • +
    +
  • +
  • List comprehensions are more powerful than map. +
      +
    • But map can be more concise for the most common cases.
    • +
    +
  • +
  • Can add arbitrary attributes to any object, class, or module.
  • +
  • The object creation syntax is nicely concise.
  • +
  • Sometimes the ability to add a bare function within a module is nice.
  • +
  • Keyword arguments are nicely done. +
      +
    • Ruby’s emulation via hashes without braces is OK, but the corner cases are problematic.
    • +
    +
  • +
  • Docstrings as part of the language is nice.
  • +
  • The new with keyword looks like a halfway-decent replacement for blocks. +
      +
    • Seems like a lot more work that using blocks and yield though.
    • +
    +
  • +
  • Python 3 drops support for octal literals starting with a 0. +
      +
    • Still allows it via a 0o prefix.
    • +
    +
  • +
  • No support for all the crazy Perl-inspired globals.
  • +
  • The names list and dictionary are better than array and hash. +
      +
    • The Ruby names are more about the implementation, especially hash.
    • +
    • I’d much prefer the name map over hash, or even dictionary.
    • +
    • The name list is only slightly better than array.
    • +
    +
  • +
  • List and string slicing is quite nice. +
      +
    • I do wish that the syntax was “..” instead of “:” though.
    • +
    • Slice assignment is even cooler.
    • +
    +
  • +
  • I prefer the Python dict syntax over the Ruby hash syntax (“:” vs. “=>”). +
      +
    • The Ruby 1.9 symbol hash syntax is an improvement, but not quite as good.
    • +
    +
  • +
  • Checking for string containment is nice: if substring in string.
  • +
+

Where Ruby Rocks

+
    +
  • Consistency.
  • +
  • Blocks.
  • +
  • Excellent functional tools to deal with Enumerable.
  • +
  • Meta-programming.
  • +
  • Optional parentheses.
  • +
  • Modules and classes are also objects.
  • +
+

 

+
+ + +
+ +
+
+ +

Debugging Pattern – Grenade

+ + + +
+

I’ve been more interested in programming patterns lately, partly due to the Ruby community’s recent interest — especially the Ruby Rogue podcast’s love affair with “Smalltalk Best Practice Patterns”.

+

I consider most of the patterns in “Smalltalk Best Practice Patterns” to be patterns “in the small” — things that are typically employed on a single line or method. The Gang Of Four patterns are more medium sized, dealing with methods and classes. The PEAA book covers architectural-scale patterns. I suppose “The Pragmatic Programmer” and similar books could (should!) be considered to be very general patterns, mostly about the practice of programming.

+

One type of pattern that I have not seen discussed much is debugging patterns. I’m not exactly sure why that is; probably just our general concentration on designing the program code itself. There are definitely testing patterns. But I can’t think of anything that I’ve seen that covers debugging patterns. A quick search on the Internet doesn’t turn up too much.

+

Anyway, a co-worker (Helena) and I were debugging recently, and were having trouble figuring out where a certain method on a certain object was being called. We came up with a really cool solution. We immediately called it a grenade, because its entire purpose is to blow up (raise an exception) and display a backtrace to show us the caller of the method that we’re looking for.

+

Here’s our implementation in Ruby:

+

+module Grenade
+  def method_under_investigation(*)
+    raise "method_under_investigation called"
+  end
+end
+
+class Xyz
+  ...
+  def xyz
+    object_under_investigation.extend(Grenade)
+  end
+  ...
+end
+

I’m sure we could wrap that in some more semantic sugar, even going as far as making it look something like this:

+

+class Xyz
+  ...
+  def xyz
+    object_under_investigation.blow_up_when_method_called(:method_under_investigation)
+  end
+  ...
+end
+
+

I’m not sure that would really be worth it though, unless we were to add it to some sort of library.

+

So that’s our contribution to the (small) list of debugging patterns.

+
+ + +
+ +
+
+ +

Write Comments For Yourself

+ + + +
+

Amos and I got in a heated discussion recently on whether we should write a single line comment to better explain some code. (The code in question was Amos’s very elegant solution to testing whether a job got sent to Resque.)

+

Amos doesn’t believe in writing comments much at all. He thinks that if you’re writing a comment, it means that you’re doing something wrong, and that you probably need to write the code more clearly.

+

I agree with that, to a point. First off, it’s not necessary to write perfect code. If you can change a class or method name to better describe what you’re doing (and more importantly, why you’re doing it) then you should definitely do so. But it’s not always worth refactoring until you get every “why” answered. More importantly, I don’t think it’s even possible to capture everything in the code that is worth capturing. For example, why did you choose this implementation, as opposed to another that might be more obvious or common?

+

After our argument, I came up with a good rule of thumb (or “pattern”):

+

Write comments for your (future) self.1

+

In other words, if your comment will help you to understand the code more quickly when you look at it in the future, then it’s a valid comment. It also means that you can assume that the reader has about as much general programming knowledge as you currently do. (Your future self will have more general knowledge, but possibly less specific knowledge of the lines of code in question. And because of this, your current solution might not make as much sense in the future. You might know of a better solution in the future, but you’ll have to know all the constraints that you had when you originally wrote the code.)

+

This is not to say that you should not write comments in clear English, that others can understand. The comment is written for a future maintainer. That may be you (which is why the rule works well), or it may be someone else. The rule is more about when to write a comment, and what level of competence you should assume of the reader.

+

1 Perhaps it should be “Write comments TO your (future) self”.

+
+ + +
+ +
+
+ +

Bulk Rename in Bash

+ + + +
+

Here’s a relatively simple way to rename a bunch of files from the command line. It uses sed within a command substitution to compute the new names from the old names.

+

In this example, we’re renaming files that start with “ABC” to start with “XYZ” instead:

+
  for i in ABC*; do mv $i $(echo $i | sed -e s/^ABC/XYZ/); done
+

You’ll have to use shell globbing (wildcards) in the first part, to determine which files will be the source of the renaming, and regular expressions in the second part to translate the old names into the new names.

+

It’s a good idea to use echo in place of mv before running this, to see what the results will be, so you don’t make a mistake.

+

Depending on the situation, you may need to adapt this for your particular needs. For example, if you have too many files to rename, you would need to use xargs. Or sed might not be up for the task of transforming the names that you want. Or you might need to use find to find files within a hierarchy. As with any UNIX idiom, there are hundreds of variations on the theme.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/author/booch/page/5.html b/public/author/booch/page/5.html new file mode 100644 index 0000000..823e057 --- /dev/null +++ b/public/author/booch/page/5.html @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + +Craig Buchek – Page 5 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Introduction

+ + + +
+

Welcome to the BoochTek blog. BoochTek, LLC is a small web development company, based in St. Louis, Missouri. We specialize in Ruby on Rails, jQuery (JavaScript), and PHP.

+

This bog will be a place where we can share some of our experiences in web development, Open Source, programming, and technology in general.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/agile.html b/public/category/agile.html new file mode 100644 index 0000000..0cff5b8 --- /dev/null +++ b/public/category/agile.html @@ -0,0 +1,468 @@ + + + + + + + + + + + + + + + +Agile – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

You Don’t Have to Be Right

+ + + +
+

Not too long ago, I was asked during a job interview “how do you convince your teammates that you’re right?” I answered with probably the most surprising answer: “I don’t.”

+

It’s taken me years to realize that being right isn’t terribly important. Especially when there’s more than one right answer — which there usually is. It’s more important to work as a team. It’s more important to be respected and to have respect for others.

+

A few weeks after that interview, I was pair programming with the person who had asked me the question. We’re both senior developers, so we both have a lot of experience and opinions based on that experience. We were writing a shell script, and we both had an idea in mind about how to write the code. I let him proceed with his idea. It was a pretty good idea; probably better than mine. But we paired very effectively. I’d let him finish some code, and I’d find a way to make it better. Then he’d find a way to make my code better. What we ended up with was so much better than my original idea, and his original idea as well. I commented on this, and he agreed. We left that pairing session feeling the high of having accomplished something rare — two very experience programmers coming up with better code than we could have imagined going in.

+

I like to think of writing code as a path towards a destination. The destination is good code that does what it’s supposed to, is readable (intention revealing), is concise, and is well-factored so as to be easy to change in the future. But there are many paths to that destination. Especially when pair programming, the code gets refined as ideas are shared, getting you closer and closer to that destination. Sometimes the different paths end up with the same code; sometimes they end up with different code. But if the code does its job well (according to those criteria), it doesn’t really matter what code you ended up with.

+

Thinking back to the original question, I think the answer is a little more nuanced. (I’m pretty sure I followed up with an explanation, but I don’t recall the details.) It truly doesn’t matter in a lot of cases. If there are different paths that lead to the same destination, and they’re all roughly equal, then my rule works. It can also work even if the first path you try (a “wrong” path) doesn’t work out, as long as it’s easy enough to try a different path. I find that the majority of day-to-day coding fits these conditions.

+

But there are some places where it’s costly to get things wrong the first time. In these cases, it’s worth spending some time thinking about and discussing the alternatives before getting started. Software architecture is the big one that comes to mind. In general, weighing the pros and cons of each option and applying previous experience works best.

+

So next time you think about trying to convince someone you’re right, fight the urge. Instead, let them show you what they’re thinking. They just might surprise you. And more importantly, you might surprise yourself.

+
+ + +
+ +
+
+ +

My First Open Space

+ + + +
+

I recently attended an Open Space hosted at work. I’d never been to an Open Space, and didn’t know what to expect. We’d been told that this was a workshop to help Engineering Managers (my role), Product Owners, and Product Analysts find better ways of working together. But due to the way an Open Space works, it evolved into something completely different — and better.

+

We’d brought in Diana Larsen to facilitate the Open Space. Diana is a stalwart of the Agile community, focusing on how people and teams interact. She literally (co-)wrote the book on Agile retrospectives. Diana was also kind enough to be our guest at an Agile LINC meetup later in the evening.

+

The morning started off with all the participants sitting in chairs arranged in a circle. Diana rang a chime, a nice soothing tone that literally set the tone for the day. (Most people didn’t seem to like it, but I thought it had its purpose.) It also acted as a call to gather in the meeting space. She then walked around the inside of the circle as she explained what was going to happen.

+

The idea behind Open Spaces came with the realization that at a conference, the “hallway track” (ad hoc discussions in the hallways) was often more valuable than the scheduled talks. So they figured out a way to capture that experience. There are only a few rules:

+
    +
  • Whoever shows up is the right group
  • +
  • Whatever happens is the only thing that could have
  • +
  • Be prepared to be surprised
  • +
  • Whenever it starts is the right time
  • +
  • When it’s over, it’s over
  • +
  • The Law of 2 Feet: If you’re not learning or contributing, go somewhere else
  • +
+

Once Diana set the scene and explained the rules, people came up and presented ideas for topics. If you proposed a topic, you chose a time and location on the topic board. At that time, you’d facilitate a discussion on that topic. The format was really conducive to discussions. There was no preparation, so discussions were from the heart — lots of people felt that they could contribute.

+

Being new to the company (only 2 months), I got a lot out of the workshop, beyond the content itself. I got to get to know several more people. I’d even say I made a few friends. Having open discussions, we found that many of the teams were having the same issues. This made it easy for me to talk to other people.

+

All-in-all, I found the Open Space to be extremely conducive to discussions aimed at identifying issues, brainstorming solutions, and planning action items. I felt like we made great strides in addressing some of the biggest problems our organization is currently facing.

+
+ + +
+ +
+
+ +

Team Values

+ + + +
+

I held a retrospective with my new team last week. The team includes 2 senior developers, 2 junior developers, a product owner, and a product analyst. I’ve joined the team as an engineering manager, which I think of more as a team lead with an elevated title. Being new to this group, I wanted a way to understand their values. What motivates them? What common values do we share that we can leverage to move forward in the same direction?

+

I started out with a pretty simple question: “What do you value (in regards to what we’re building); what are you willing to fight for?” I asked them each to write down several values and then put them on the board before looking at everyone else’s answers. I also asked each person to rank their values in order of importance.

+

It turned out that my question was a little vague. Some people thought about the question in terms of the end result, and some in terms of the process of creating the software. In some ways that was a bit of a problem, because the different interpretations led people in different directions. But in other ways, not pushing them in any particular direction got more varied answers, exposing how they think about the project and the product.

+

My list (in order) was Effectiveness (doing the right thing), Quality (doing things right the first time), Happiness, Purpose, and Teamwork. In retrospect, I should have put Happiness first. If I’m not happy at work, I don’t really want to be there, and need to move on. (Sometimes I can trade some happiness at work for more happiness at home, but I’m definitely a person that needs to be happy at work.) The other values serve to improve my happiness, but the happiness is more important.

+

My own issue ranking my values turned out to be a problem with the ranking in general. Should they be ranked in importance of the necessity of the value as an outcome, or in importance of the necessity to focus on the value? I think the former is what I was looking for, but even I wasn’t clear on that when I began the exercise.

+

After everyone put their values up on the board, we read them off. Then I asked the team to choose several values that we share as a team. We pulled them off the individual members’ lists, and put them in the team list. Then I asked each person to come up and rank those values, then explain why they had ordered them that way, especially when they ordered them significantly different than the last person. I was hoping to come to some convergence of the rank over time, so we could document our values in rank order. That didn’t happen. But the discussion was illuminating to me and to everyone on the team.

+

I think the most interesting part about the lack of convergence was the difference between the developers and the product guys. The product guys definitely viewed the values more in terms of outcomes than the process. That makes sense — they’re not as intimately involved in the process of building the product.

+

We were able to converge on the top priority though: Will the user buy the product? This was a combination of a couple different values that we merged together. This included the end user experience as well as making sure the team would continue to have a reason for existing. The rest of the values we left unordered: Teamwork (cohesiveness), Data driven decisions, Team ownership, Simplicity, Effectiveness, Maintainability / Supportability, Quality, Automation, and Performance. I think that’s a pretty decent list.

+

As a couple teammates pointed out, those values are probably in part a reflection of this current point in time. If I asked the same question some other time, under different conditions and team dynamics, the answers would probably change a bit. And we’d probably come up with other answers if asked again, just due to randomness of the way we think about these things.

+

But I don’t think I’d do this activity a second time with the team. It was really about understanding our motivations — both our own, and those of our teammates. I found it effective in that way, and also in helping the team to think about our culture and how we can work to shape it to help us all push in the same direction.

+

There are a few caveats. When I asked for feedback on the exercise, one teammate pointed out that it wouldn’t work if people weren’t honest about their values, and they answered with what they thought management or their teammates wanted to hear. I don’t think that was an issue with this group, but it’s something to keep in mind.

+

The other major thing I’d do is to make it clear up front that I’m not looking for any action items from this activity; it’s more about understanding each other and ourselves. And next time, I’ll work to clarify how to rank the values.

+

I would recommend this activity for a new team, or when the makeup of the team is changing in some significant way. I wish there was a way to help a team converge on the ranking of their values, but I suppose I should be happy that agreeing on the set of important values went pretty quickly. And the diversity of ideas and opinions is probably a blessing that I should be embracing — the more ideas we have, the wider the variety of solutions we can imagine.

+
+ + +
+ +
+
+ +

2015 Year in Review

+ + + +
+

It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+
+ + +
+ +
+
+ +

Face Your Fears

+ + + +
+

I’ve always been someone who faces my fears.

+

I have a moderate case of arachnophobia. I don’t run away when I see a spider, but it creeps my out when one is crawling on me. When I was in college, I decided to buy a tarantula to attempt to get over my irrational fear of spiders. I thought I’d be able to get more comfortable with the tarantula over time, eventually to the point of letting it crawl on my arm. It didn’t work. Although I did find that my fear of tarantulas is rational — I got a terrible case of hives just from touching the urticating hairs that fell off into its water sponge.

+

Last week, I was on vacation in Mexico. One of the excursions we took involved jumping in the water a lot. I’m not a strong swimmer — mostly because I have a hard time closing my nose; I hold my nose when I jump in. We zip-lined into the water a lot. At one point, there was a cliff to dive into the water from. It was about 15 feet above the water. It didn’t look so far down before jumping. But it felt like a really long way down the first time I jumped from it. It was pretty scary for me. So I did it a second time. There wasn’t any peer pressure to jump a second time. I literally jumped a second time specifically because I was scared.

+

Fear is a weird thing. Fear is there to protect us. But it’s there to protect us from the dangers of the African savannah. Most of the things our fears protect us from don’t exist in our everyday modern lives. So maybe we should work to gain a better understanding of how our fears work, to figure out when to pay attention to them and when to ignore them.

+

Fortunately, our brain has a good mechanism to help us do this. Our brains basically have 2 main processing systems. The first one is for quick reactions. This one involves things that are nearly reflexes. Fear is in this system. The second system is our analytical reasoning system. This system takes longer to process, but is able to take on more information.

+

Whenever the situation allows us time for both systems to work, we need to listen to them both. We need to listen to our fears, because they’re there for a reason. But that reason might not pertain to our situation. So we need to realize that, and let the slow analytical system determine if we should ignore our fears.

+

If we don’t allow both systems to work, we’re not taking full advantage of our brains; we’re not taking full advantage of the situations that life is presenting to us.

+
+ + +
+ +
+
+ +

Encouragement

+ + + +
+

I’ve been on vacation the past week, in Cozumel, Mexico. One day, we went on an excursion called Xenotes. A cenote (say-NO-tay) is a sinkhole filled with fresh water. (The “X” is to give it a more Mayan-sounding trademarkable name.) We had a lot of fun swimming, kayaking, and zip-lining. A bilingual tour guide led our group, which consisted of people from across the US and South America, of various ages and physical abilities.

+

Our tour guide was a lot of fun. He made jokes, told us about the cenotes, and led us in the activities. But he also encouraged us. When someone was scared to do something, he was supportive. He told us that it was okay, and we could do it. But we also felt like it was OK to fail, if we really couldn’t. It really felt like his encouragement was literally creating courage.

+

What was really neat was that despite the language barrier, everyone else was also supportive and encouraging. Everyone cheered with encouragement before someone would attempt something difficult. And we’d cheer especially loud once someone accomplished something that was difficult for their abilities.

+

It was an awesome feeling to feel so supported. It made me feel like I was in a safe place, where I could try new things that were a little past my comfort zone. I was able to do the zip-line upside-down. I jumped off a 15-foot cliff into the water. I even jumped off the cliff a second time, even though I was a little scared.

+

Back at the resort, we played some volleyball in the pool. It was a similar situation, with players of varying ages and abilities. Again, we tried to help the weaker players feel comfortable so they could improve without feeling judged or self-conscious. It helped everyone have a good time. It made everything more fun to be in such a supportive environment, whether I was in a position as one of the more skilled (volleyball), or one of the less skilled (jumping or zip-lining into the water). We were all able to accomplish more, with less effort.

+

These experiences provide a good lesson that can be applied in a lot of places. Such a supportive environment would help any relationship, and any team. I’ve been on a couple really good software development teams, but I don’t think any of them have been as supportive as these two groups of strangers.

+

I’ve decided that this should be one of my goals as a team leader. I want to create an encouraging environment for the whole team. I want to make sure that everyone is comfortable enough that they feel like they can try things that are difficult, even if they might fail (as long as nobody gets hurt).

+

If a group of strangers can do this, so can your team. So can you and your significant others. We need to work every day to make sure that we’re supporting each other. It’s the best way to get everyone to achieve more.

+
+ + +
+ +
+
+ +

Show and Tell

+ + + +
+

I’m wrapping up my current consulting gig at Mercy in December, and starting a new gig at CenturyLink. I’m a software developer, but that’s only a part of what I do. What I really do is join a team and help them improve the way they work — both their processes and their technical skills.

+

I think this is a key differentiator for me as a consultant. Most consultants (and Agile coaches) come in and tell people what to do. I don’t like to just tell people what to do. I’d much prefer to work side-by-side with them, getting a better understanding of what their challenges are. Once I have a better understanding of the challenges, I’m able to better brainstorm some ideas to try. Then we can experiment to see what will work and what won’t.

+

Instead of telling people what to do, I show them how. Most people learn better from seeing than from hearing. They also learn better if you explain how and why, not just what. So showing them how to do something is more effective than telling them. By showing and doing, you can also set a good example. This is especially important when collaboration is a large part of what needs to be improved.

+

I’ve found that this style of consulting is more highly respected by everyone. I build trust with developers by working closely with them. Managers like to keep me around once they see how effective these methods can be, so the gigs I take on tend to last relatively long.

+

The biggest problem I have is explaining how this works. I don’t really know what to put on my résumé. Sometimes I call myself an Agile practitioner, and sometimes an Agile player/coach. But those aren’t terribly satisfying descriptions. I’m considering actually putting “I help teams improve the way they work — both their processes and their technical skills” on the résumé. But that seems awkward, and misses the show versus tell part. I’d be open to any suggestions.

+

 

+
+ + +
+ +
+
+ +

Happiness Retrospective

+ + + +
+

I facilitated a retrospective today; it was one of the best retros I’ve ever been involved with. I figured out what activities I wanted to do earlier in the morning. They were really quite simple. I wanted to focus on happiness.

+

How happy are you at work?

+

I started with two questions that I’ve used with teams before, to some success (although not so successful for one particular team). The first question I asked was “How happy are you at work?” I had them put a rating from 0 to 10, with 0 meaning they should have quit last week, and 10 meaning they couldn’t imaging being happier at work.

+

The answers were mostly 7s and 8s, with a 5 and a 9. The average came to 7.5, which is pretty good. The 5 concerns me a bit, especially that it’s 2 points lower than anyone else’s answer.

+

How effective do you think the teams is?

+

The next question I asked was “How effective do you think the teams is?”. Again, from 0 to 10, with 0 meaning they can’t accomplish anything, and 10 meaning you couldn’t imagine a more effective team.

+

When I ask both of these questions, the scores are always highly correlated. If your team isn’t doing good work, it’ll make you unhappy. And if you are unhappy, you’re less likely to do your best work. This team was no different; the 5 and 9 became a 6 and a 10, and most of the 7s became 8s, for an average of just under 8.

+

What makes you happy at work?

+

The next question I asked was “What makes you happy at work?”. The answers were mostly about the teamwork and teammates. This went quicker than I expected. At this point, I was worried the retro would only last a little more than 30 minutes.

+

What would make you happier at work?

+

The final question I asked was “What would make you happier at work?”. This was the real pay-off. We spent about half an hour just talking about the things that would make us happier, and what we could do to improve our happiness. We came up with 10 potential action items. I usually limit teams to trying 3 or 4 action items, but most of the items are quite small, so we’re going to try 6 of them. One is just observing another team’s standup meetings, to see how they’re using their time effectively.

+

Everyone went away feeling that this was a really good retro. It felt good to focus on happiness. Happiness is something I’ve been talking a lot about on the This Agile Life podcast, and it felt good to take some action on it. I’ve done “positive-only” retros before, but this one felt even better than that, by specifically targeting happiness and how we can achieve it.

+
+ + +
+ +
+
+ +

Impromptu Retrospective

+ + + +
+

I’m surprised that I haven’t gotten this story down in print before. It’s something I’ve mentioned many times — including a few times on the podcast. It’s a great story about the power of retrospectives, and it’s a great story about the power of a blameless post-mortem.

+

I don’t recall all the specifics at this point. It was about 5 years ago. I’d just noticed that Arun had made some sort of mistake. That’s fine, people make mistakes. The thing that was different about his mistake was that I had made the same mistake about a week prior. And Amos had made the same mistake about a week before that.

+

Noticing a pattern of mistakes, Amos and I called an impromptu retrospective. We gathered all the developers into a conference room. We explained the problem that we were running into. At first, Arun was defensive. That’s understandable; he thought we were there to come down on him, to lay blame. But we made it clear that we weren’t focusing on him. We admitted that we had also made the same mistake recently. We weren’t there to lay blame; we were there to figure out how our team could stop making the mistake. It took Arun a few minutes to get over the defensiveness.

+

With the defensiveness out of the way, we could focus on the issue at hand. We were able to figure out the root cause of us all making the mistake. (I don’t know if we played the “5 whys” game, but I’m sure we effectively did something similar.) And with that, we were able to change our process, so that nobody else would make the same mistake again.

+

There are 2 important points to this story. First, you don’t have to wait until a scheduled retrospective to hold a retrospective. This one was impromptu, and it’s the best one we ever had. We saw a problem, addressed it, and found a solution in less than an hour. Had we waited until the end of the week, we would have forgotten some of the details, and wouldn’t have been as effective at solving the problem. Second, when addressing problems, take your ego out of the equation. If you’re in a position of authority, take the blame — but never place blame. Focus on what’s important — solving the problem.

+

And don’t forget the Retrospective Prime Directive:

+

Regardless of what we discover, we understand and truly believe that everyone did the best job they could, given what they knew at the time, their skills and abilities, the resources available, and the situation at hand.

+

 

+
+ + +
+ +
+
+ +

The Problem With Estimates

+ + + +
+
+
+

I’m a big proponent of Agile (mostly XP; I’m mostly anti-Scrum) and I’ve contributed some to the #noestimates “movement”.

+

I don’t really mean that nobody should ever estimate anything. I mean that I’ve never seen useful (fine-grained) estimates anywhere. Here are some of the problems with estimates that I’ve seen frequently:

+
    +
  1. We’re not good at estimating how long things will take. We’re usually optimistic about how quickly we can get things done, and almost always miss thinking about things that will take more time. I’ve never seen a case where a project is completed more quickly than estimated. I’ve only rarely seen fine-grained (story-level) tasks completed more quickly than estimated.
  2. +
  3. Management asks for estimates and then treats them as deadlines. The team then learns to inflate their estimates. Then management learns to reduce the estimates they’re given. Given fudge factors in each direction, the estimate no longer has much reliability. Even if you’re using story points, the point inflation/deflation leads to less consistency and therefore reduced reliability.
  4. +
  5. Estimates that are given are negotiated down, or simply reduced. This leads to the question why you’d ask for an estimate and not take the answer provided. If you’re not going to listen to the answer, why are you asking the question? This is probably the craziest one on the list — given my first point, increasing an estimate would make sense. Reducing the estimates is just magical wishful thinking.
  6. +
  7. Plans change and work is added, but the deadline (presumably based on the estimates) is not changed to correspond with the extra work involved. So again, you’re not actually even using the estimates that were given.
  8. +
  9. Management dictates deadlines arbitrarily, without speaking to the people who will be doing the work. Spending time estimating how long each task will take when the deadline is already set is completely pointless.
  10. +
  11. Almost every deadline is complete bullshit, based on nothing. Often the excuse is that marketing needs to know when something will come out, so that they can let people know about it. Why they need to know the exact release date way in advance, I’ve never been able to figure out. Many people intuitively know that the deadlines are bullshit, and will likely be allowed to slip. The only exception to bullshit deadlines I’ve come across are regulatory deadlines. (I know there are a few other exceptions out there.)
  12. +
  13. Estimation at a fine-grained level isn’t necessary. Many Agile teams estimate using story points, and determine a conversion from story points to time based on previous empirical data. This is fine, except that the time spent estimating the story is wasted time — counting the number of stories almost always gives the same predictive power. Teams tend to get better at breaking up stories over time, so that they’re more consistent in size, so this becomes more likely over time.
  14. +
  15. The ultimate purpose of an estimate is to evaluate whether the proposed work will be profitable, and therefore worth doing. Or to compare the ROI (return on investment) between alternative projects. But to know that, you’ll have to know what value that work will provide. I don’t believe I’ve ever seen that done — at least not at a fine-grained level. Usually by the time you’re asked to estimate, the project has already gotten approval to proceed.
  16. +
+

I’ll note that most of these pit management against the team, instead of working together toward a common cause. Most of the practices also lead to seriously demoralizing the team. And most of the time, the estimates aren’t really even taken into account very much.

+

My advice is to first understand the value of a project before you consider estimating the costs. The estimation at this point will be very rough, so make sure that you have a very wide margin between the expected value and the rough estimate of the cost. If you’re pretty certain of the expected value, I’d probably want to make sure I could still be profitable even if it took 3 or 4 times as long to complete as the rough estimate. And if there’s uncertainty in the expected value, much more.

+

Another way to mitigate the risk of throwing money at something that’s not going to have positive ROI is to reduce the feedback loop. Order the work so that the tasks are ranked in order of value to the customer. (Realistically, you’ll have dependencies of tasks to worry about, and should consider effort involved too.) So work on the most valuable feature first — get that out into production as soon as possible. Once that’s done, you can assess if your ROI is positive or not. Keep iterating in this fashion, working on the features that will provide the most value first. Keep assessing your ROI, and stop when the ROI is no longer worth it, compared to other projects the team could be working on.

+

At a fine-grained level, if you’re using story points, I’d ask you to do the math to see if just counting the stories would be as effective at predicting how much will be done over time as using the story points. If so, you can save the time the team spends on estimating stories. I’d still recommend spending time talking about stories so that everyone has a shared understanding of what needs to be done, and to break stories up into a smaller, more manageable size — with one acceptance criteria per story. Also take a look to see if empirical average cycle time (how long it takes a single story to move from start to finish) might provide you the predictive power just as well as estimates. (I.e. is it bandwidth or latency that really provides the predictive power you’re looking for?)

+

And don’t forget Hofstadter’s Law: It always takes longer than you expect, even when you take into account Hofstadter’s Law.

+
+
+
+
+
+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/agile/empathy.html b/public/category/agile/empathy.html new file mode 100644 index 0000000..f3407ea --- /dev/null +++ b/public/category/agile/empathy.html @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + +Empathy – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

You Don’t Have to Be Right

+ + + +
+

Not too long ago, I was asked during a job interview “how do you convince your teammates that you’re right?” I answered with probably the most surprising answer: “I don’t.”

+

It’s taken me years to realize that being right isn’t terribly important. Especially when there’s more than one right answer — which there usually is. It’s more important to work as a team. It’s more important to be respected and to have respect for others.

+

A few weeks after that interview, I was pair programming with the person who had asked me the question. We’re both senior developers, so we both have a lot of experience and opinions based on that experience. We were writing a shell script, and we both had an idea in mind about how to write the code. I let him proceed with his idea. It was a pretty good idea; probably better than mine. But we paired very effectively. I’d let him finish some code, and I’d find a way to make it better. Then he’d find a way to make my code better. What we ended up with was so much better than my original idea, and his original idea as well. I commented on this, and he agreed. We left that pairing session feeling the high of having accomplished something rare — two very experience programmers coming up with better code than we could have imagined going in.

+

I like to think of writing code as a path towards a destination. The destination is good code that does what it’s supposed to, is readable (intention revealing), is concise, and is well-factored so as to be easy to change in the future. But there are many paths to that destination. Especially when pair programming, the code gets refined as ideas are shared, getting you closer and closer to that destination. Sometimes the different paths end up with the same code; sometimes they end up with different code. But if the code does its job well (according to those criteria), it doesn’t really matter what code you ended up with.

+

Thinking back to the original question, I think the answer is a little more nuanced. (I’m pretty sure I followed up with an explanation, but I don’t recall the details.) It truly doesn’t matter in a lot of cases. If there are different paths that lead to the same destination, and they’re all roughly equal, then my rule works. It can also work even if the first path you try (a “wrong” path) doesn’t work out, as long as it’s easy enough to try a different path. I find that the majority of day-to-day coding fits these conditions.

+

But there are some places where it’s costly to get things wrong the first time. In these cases, it’s worth spending some time thinking about and discussing the alternatives before getting started. Software architecture is the big one that comes to mind. In general, weighing the pros and cons of each option and applying previous experience works best.

+

So next time you think about trying to convince someone you’re right, fight the urge. Instead, let them show you what they’re thinking. They just might surprise you. And more importantly, you might surprise yourself.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/agile/estimation-agile.html b/public/category/agile/estimation-agile.html new file mode 100644 index 0000000..15bf3ff --- /dev/null +++ b/public/category/agile/estimation-agile.html @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + +Estimation – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

The Problem With Estimates

+ + + +
+
+
+

I’m a big proponent of Agile (mostly XP; I’m mostly anti-Scrum) and I’ve contributed some to the #noestimates “movement”.

+

I don’t really mean that nobody should ever estimate anything. I mean that I’ve never seen useful (fine-grained) estimates anywhere. Here are some of the problems with estimates that I’ve seen frequently:

+
    +
  1. We’re not good at estimating how long things will take. We’re usually optimistic about how quickly we can get things done, and almost always miss thinking about things that will take more time. I’ve never seen a case where a project is completed more quickly than estimated. I’ve only rarely seen fine-grained (story-level) tasks completed more quickly than estimated.
  2. +
  3. Management asks for estimates and then treats them as deadlines. The team then learns to inflate their estimates. Then management learns to reduce the estimates they’re given. Given fudge factors in each direction, the estimate no longer has much reliability. Even if you’re using story points, the point inflation/deflation leads to less consistency and therefore reduced reliability.
  4. +
  5. Estimates that are given are negotiated down, or simply reduced. This leads to the question why you’d ask for an estimate and not take the answer provided. If you’re not going to listen to the answer, why are you asking the question? This is probably the craziest one on the list — given my first point, increasing an estimate would make sense. Reducing the estimates is just magical wishful thinking.
  6. +
  7. Plans change and work is added, but the deadline (presumably based on the estimates) is not changed to correspond with the extra work involved. So again, you’re not actually even using the estimates that were given.
  8. +
  9. Management dictates deadlines arbitrarily, without speaking to the people who will be doing the work. Spending time estimating how long each task will take when the deadline is already set is completely pointless.
  10. +
  11. Almost every deadline is complete bullshit, based on nothing. Often the excuse is that marketing needs to know when something will come out, so that they can let people know about it. Why they need to know the exact release date way in advance, I’ve never been able to figure out. Many people intuitively know that the deadlines are bullshit, and will likely be allowed to slip. The only exception to bullshit deadlines I’ve come across are regulatory deadlines. (I know there are a few other exceptions out there.)
  12. +
  13. Estimation at a fine-grained level isn’t necessary. Many Agile teams estimate using story points, and determine a conversion from story points to time based on previous empirical data. This is fine, except that the time spent estimating the story is wasted time — counting the number of stories almost always gives the same predictive power. Teams tend to get better at breaking up stories over time, so that they’re more consistent in size, so this becomes more likely over time.
  14. +
  15. The ultimate purpose of an estimate is to evaluate whether the proposed work will be profitable, and therefore worth doing. Or to compare the ROI (return on investment) between alternative projects. But to know that, you’ll have to know what value that work will provide. I don’t believe I’ve ever seen that done — at least not at a fine-grained level. Usually by the time you’re asked to estimate, the project has already gotten approval to proceed.
  16. +
+

I’ll note that most of these pit management against the team, instead of working together toward a common cause. Most of the practices also lead to seriously demoralizing the team. And most of the time, the estimates aren’t really even taken into account very much.

+

My advice is to first understand the value of a project before you consider estimating the costs. The estimation at this point will be very rough, so make sure that you have a very wide margin between the expected value and the rough estimate of the cost. If you’re pretty certain of the expected value, I’d probably want to make sure I could still be profitable even if it took 3 or 4 times as long to complete as the rough estimate. And if there’s uncertainty in the expected value, much more.

+

Another way to mitigate the risk of throwing money at something that’s not going to have positive ROI is to reduce the feedback loop. Order the work so that the tasks are ranked in order of value to the customer. (Realistically, you’ll have dependencies of tasks to worry about, and should consider effort involved too.) So work on the most valuable feature first — get that out into production as soon as possible. Once that’s done, you can assess if your ROI is positive or not. Keep iterating in this fashion, working on the features that will provide the most value first. Keep assessing your ROI, and stop when the ROI is no longer worth it, compared to other projects the team could be working on.

+

At a fine-grained level, if you’re using story points, I’d ask you to do the math to see if just counting the stories would be as effective at predicting how much will be done over time as using the story points. If so, you can save the time the team spends on estimating stories. I’d still recommend spending time talking about stories so that everyone has a shared understanding of what needs to be done, and to break stories up into a smaller, more manageable size — with one acceptance criteria per story. Also take a look to see if empirical average cycle time (how long it takes a single story to move from start to finish) might provide you the predictive power just as well as estimates. (I.e. is it bandwidth or latency that really provides the predictive power you’re looking for?)

+

And don’t forget Hofstadter’s Law: It always takes longer than you expect, even when you take into account Hofstadter’s Law.

+
+
+
+
+
+
+ + +
+ +
+
+ +

Estimation Isn’t Agile

+ + + +
+

I don’t believe that estimation should be part of any Agile practice.

+

One of our managers recently mentioned that we hadn’t met the “contract” that we had “committed” to in our last iteration. This was complete nonsense, because A) we hadn’t made any such commitments, and B) we completed many more story points than the previous iterations (and without inflating story points).

+

estimates-as-deadlines

+

But her language made me come to several realizations. First and foremost, estimates are contracts. Sure, they’re not supposed to be treated as commitments, but they almost always are. And what does the Agile Manifesto say about this? It says that we should value customer collaboration over contract negotiation, and responding to change over following a plan. So it’s pretty clear that treating estimates as commitments is completely counter to the Agile values.

+

Why does this matter? What benefits do the Agile values bring us? I think the biggest benefit they bring is changing the way that we work, so that we can better deliver value to our customers. Without Agile, we’d just keep working the way we’ve always done things. And that didn’t seem to be working out so well. If we follow the Agile values and principles, at least we’ll have a fighting chance of improving our ability to deliver value.

+

Ask yourself — have you ever seen a software development project that was on time and on budget? Where the estimates were spot-on? Of course not. For one, we’re terrible at estimating. For another, our plans change — either from external factors, or from what we learn as we go.

+

Improved Estimation

+

To me, Agile is also about facing reality — and embracing it. It realizes that we’re terrible at estimating. It realizes that plans change. Most Agile methodologies have some tricks to counteract Hofstadter’s law. Generally, we use relative story points instead of hours, and then use an empirical factor to convert points to hours.

+

When this works, it is better than any other estimation I’ve ever seen. But it doesn’t work very often. People have trouble with relative estimation. How do you set the basis for what a point means without relating it to actual hours? Affinity estimation could work, but then you have to remember what the basis was. We’ve got a large distributed team, and when we tried this, we couldn’t all remember what the basis was.

+

Since we couldn’t get affinity estimation to work, we tried changing to perfect hours (only powers of 2). But then people thought of them as time. When we took longer than the estimate on an individual story, managers and team members thought we were taking longer than we should have. So our estimates ended up causing problems.

+

What Can We Do Instead?

+

Managers want estimates so that they can have predictability. They want to know when new features will be available. Is there a better way to get what we need?

+

I believe there’s a better way — prioritization. If you work on the most important thing first, then the most important thing will get done first. We should always be working on the next most important thing.

+

What if there’s more than 1 thing that’s most important? Then you’ve failed. You’ve failed at logic if you can’t understand that only 1 thing can be most important. You’ve failed at prioritizing the customers’ needs. You’ve failed at project management.

+

Arguments

+

1. Why can’t you just tell us how long it will really take?

+

Because we don’t know. Because we can’t know. This is the first time we’ve ever implemented the functionality you’ve asked for. If we’d done it before, we’d just use that existing code. As Glenn Vanderburg pointed out in his excellent talk on Software Engineering, we’re not building software, we’re architecting it.

+

2. But we have to tell our customers what to expect.

+

Why? Is the product so bad that you can’t keep customers around without leading them on with future enhancements? And why do customers need exact dates? A general roadmap telling them what the priorities for upcoming features should be sufficient.

+

3. But we have to have messaging about new features.

+

OK. Then send out that messaging once the feature has made it to Staging. Or even after it’s been rolled out to Production.

+

4. But we’ve promised these new features to the customers by this date.

+

Ah, so you’ve made promises to the customer that you don’t have control over. Have you ever heard of “under-promise and over-deliver”? That’s how you create happy customers. Yet you’ve done just the opposite, haven’t you? And then you want to blame someone else.

+

Risk

+

Estimates are risk. But the risk doesn’t come at the end, when the estimates are shown to be incorrect. The risk was in asking for the estimates in the first place, and placing trust in them. Don’t do it. Don’t promise things that you can’t be sure of.

+

Embrace this reality. Embrace this uncertainty. Always focus on what’s most important. That’s how you make customers happy.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/agile/estimation-agile/feed b/public/category/agile/estimation-agile/feed new file mode 100644 index 0000000..c0ac377 --- /dev/null +++ b/public/category/agile/estimation-agile/feed @@ -0,0 +1,103 @@ + + + + Estimation – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + The Problem With Estimates + http://blog.boochtek.com/2015/09/28/no-estimates + http://blog.boochtek.com/2015/09/28/no-estimates#comments + Tue, 29 Sep 2015 03:39:41 +0000 + + + + + http://blog.boochtek.com/?p=261 + Continue reading "The Problem With Estimates"]]> + +
+

I’m a big proponent of Agile (mostly XP; I’m mostly anti-Scrum) and I’ve contributed some to the #noestimates “movement”.

+

I don’t really mean that nobody should ever estimate anything. I mean that I’ve never seen useful (fine-grained) estimates anywhere. Here are some of the problems with estimates that I’ve seen frequently:

+
    +
  1. We’re not good at estimating how long things will take. We’re usually optimistic about how quickly we can get things done, and almost always miss thinking about things that will take more time. I’ve never seen a case where a project is completed more quickly than estimated. I’ve only rarely seen fine-grained (story-level) tasks completed more quickly than estimated.
  2. +
  3. Management asks for estimates and then treats them as deadlines. The team then learns to inflate their estimates. Then management learns to reduce the estimates they’re given. Given fudge factors in each direction, the estimate no longer has much reliability. Even if you’re using story points, the point inflation/deflation leads to less consistency and therefore reduced reliability.
  4. +
  5. Estimates that are given are negotiated down, or simply reduced. This leads to the question why you’d ask for an estimate and not take the answer provided. If you’re not going to listen to the answer, why are you asking the question? This is probably the craziest one on the list — given my first point, increasing an estimate would make sense. Reducing the estimates is just magical wishful thinking.
  6. +
  7. Plans change and work is added, but the deadline (presumably based on the estimates) is not changed to correspond with the extra work involved. So again, you’re not actually even using the estimates that were given.
  8. +
  9. Management dictates deadlines arbitrarily, without speaking to the people who will be doing the work. Spending time estimating how long each task will take when the deadline is already set is completely pointless.
  10. +
  11. Almost every deadline is complete bullshit, based on nothing. Often the excuse is that marketing needs to know when something will come out, so that they can let people know about it. Why they need to know the exact release date way in advance, I’ve never been able to figure out. Many people intuitively know that the deadlines are bullshit, and will likely be allowed to slip. The only exception to bullshit deadlines I’ve come across are regulatory deadlines. (I know there are a few other exceptions out there.)
  12. +
  13. Estimation at a fine-grained level isn’t necessary. Many Agile teams estimate using story points, and determine a conversion from story points to time based on previous empirical data. This is fine, except that the time spent estimating the story is wasted time — counting the number of stories almost always gives the same predictive power. Teams tend to get better at breaking up stories over time, so that they’re more consistent in size, so this becomes more likely over time.
  14. +
  15. The ultimate purpose of an estimate is to evaluate whether the proposed work will be profitable, and therefore worth doing. Or to compare the ROI (return on investment) between alternative projects. But to know that, you’ll have to know what value that work will provide. I don’t believe I’ve ever seen that done — at least not at a fine-grained level. Usually by the time you’re asked to estimate, the project has already gotten approval to proceed.
  16. +
+

I’ll note that most of these pit management against the team, instead of working together toward a common cause. Most of the practices also lead to seriously demoralizing the team. And most of the time, the estimates aren’t really even taken into account very much.

+

My advice is to first understand the value of a project before you consider estimating the costs. The estimation at this point will be very rough, so make sure that you have a very wide margin between the expected value and the rough estimate of the cost. If you’re pretty certain of the expected value, I’d probably want to make sure I could still be profitable even if it took 3 or 4 times as long to complete as the rough estimate. And if there’s uncertainty in the expected value, much more.

+

Another way to mitigate the risk of throwing money at something that’s not going to have positive ROI is to reduce the feedback loop. Order the work so that the tasks are ranked in order of value to the customer. (Realistically, you’ll have dependencies of tasks to worry about, and should consider effort involved too.) So work on the most valuable feature first — get that out into production as soon as possible. Once that’s done, you can assess if your ROI is positive or not. Keep iterating in this fashion, working on the features that will provide the most value first. Keep assessing your ROI, and stop when the ROI is no longer worth it, compared to other projects the team could be working on.

+

At a fine-grained level, if you’re using story points, I’d ask you to do the math to see if just counting the stories would be as effective at predicting how much will be done over time as using the story points. If so, you can save the time the team spends on estimating stories. I’d still recommend spending time talking about stories so that everyone has a shared understanding of what needs to be done, and to break stories up into a smaller, more manageable size — with one acceptance criteria per story. Also take a look to see if empirical average cycle time (how long it takes a single story to move from start to finish) might provide you the predictive power just as well as estimates. (I.e. is it bandwidth or latency that really provides the predictive power you’re looking for?)

+

And don’t forget Hofstadter’s Law: It always takes longer than you expect, even when you take into account Hofstadter’s Law.

+
+ +
+
+
+]]>
+ http://blog.boochtek.com/2015/09/28/no-estimates/feed + 2 +
+ + Estimation Isn’t Agile + http://blog.boochtek.com/2014/03/23/agile-estimation + http://blog.boochtek.com/2014/03/23/agile-estimation#comments + Mon, 24 Mar 2014 04:02:28 +0000 + + + + + + + http://blog.boochtek.com/?p=161 + Continue reading "Estimation Isn’t Agile"]]> + I don’t believe that estimation should be part of any Agile practice.

+

One of our managers recently mentioned that we hadn’t met the “contract” that we had “committed” to in our last iteration. This was complete nonsense, because A) we hadn’t made any such commitments, and B) we completed many more story points than the previous iterations (and without inflating story points).

+

estimates-as-deadlines

+

But her language made me come to several realizations. First and foremost, estimates are contracts. Sure, they’re not supposed to be treated as commitments, but they almost always are. And what does the Agile Manifesto say about this? It says that we should value customer collaboration over contract negotiation, and responding to change over following a plan. So it’s pretty clear that treating estimates as commitments is completely counter to the Agile values.

+

Why does this matter? What benefits do the Agile values bring us? I think the biggest benefit they bring is changing the way that we work, so that we can better deliver value to our customers. Without Agile, we’d just keep working the way we’ve always done things. And that didn’t seem to be working out so well. If we follow the Agile values and principles, at least we’ll have a fighting chance of improving our ability to deliver value.

+

Ask yourself — have you ever seen a software development project that was on time and on budget? Where the estimates were spot-on? Of course not. For one, we’re terrible at estimating. For another, our plans change — either from external factors, or from what we learn as we go.

+

Improved Estimation

+

To me, Agile is also about facing reality — and embracing it. It realizes that we’re terrible at estimating. It realizes that plans change. Most Agile methodologies have some tricks to counteract Hofstadter’s law. Generally, we use relative story points instead of hours, and then use an empirical factor to convert points to hours.

+

When this works, it is better than any other estimation I’ve ever seen. But it doesn’t work very often. People have trouble with relative estimation. How do you set the basis for what a point means without relating it to actual hours? Affinity estimation could work, but then you have to remember what the basis was. We’ve got a large distributed team, and when we tried this, we couldn’t all remember what the basis was.

+

Since we couldn’t get affinity estimation to work, we tried changing to perfect hours (only powers of 2). But then people thought of them as time. When we took longer than the estimate on an individual story, managers and team members thought we were taking longer than we should have. So our estimates ended up causing problems.

+

What Can We Do Instead?

+

Managers want estimates so that they can have predictability. They want to know when new features will be available. Is there a better way to get what we need?

+

I believe there’s a better way — prioritization. If you work on the most important thing first, then the most important thing will get done first. We should always be working on the next most important thing.

+

What if there’s more than 1 thing that’s most important? Then you’ve failed. You’ve failed at logic if you can’t understand that only 1 thing can be most important. You’ve failed at prioritizing the customers’ needs. You’ve failed at project management.

+

Arguments

+

1. Why can’t you just tell us how long it will really take?

+

Because we don’t know. Because we can’t know. This is the first time we’ve ever implemented the functionality you’ve asked for. If we’d done it before, we’d just use that existing code. As Glenn Vanderburg pointed out in his excellent talk on Software Engineering, we’re not building software, we’re architecting it.

+

2. But we have to tell our customers what to expect.

+

Why? Is the product so bad that you can’t keep customers around without leading them on with future enhancements? And why do customers need exact dates? A general roadmap telling them what the priorities for upcoming features should be sufficient.

+

3. But we have to have messaging about new features.

+

OK. Then send out that messaging once the feature has made it to Staging. Or even after it’s been rolled out to Production.

+

4. But we’ve promised these new features to the customers by this date.

+

Ah, so you’ve made promises to the customer that you don’t have control over. Have you ever heard of “under-promise and over-deliver”? That’s how you create happy customers. Yet you’ve done just the opposite, haven’t you? And then you want to blame someone else.

+

Risk

+

Estimates are risk. But the risk doesn’t come at the end, when the estimates are shown to be incorrect. The risk was in asking for the estimates in the first place, and placing trust in them. Don’t do it. Don’t promise things that you can’t be sure of.

+

Embrace this reality. Embrace this uncertainty. Always focus on what’s most important. That’s how you make customers happy.

+]]>
+ http://blog.boochtek.com/2014/03/23/agile-estimation/feed + 3 +
+
+
diff --git a/public/category/agile/feed b/public/category/agile/feed new file mode 100644 index 0000000..754380c --- /dev/null +++ b/public/category/agile/feed @@ -0,0 +1,301 @@ + + + + Agile – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + You Don’t Have to Be Right + http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right + http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right#respond + Thu, 07 Jul 2016 04:22:08 +0000 + + + + + http://blog.boochtek.com/?p=313 + Continue reading "You Don’t Have to Be Right"]]> + Not too long ago, I was asked during a job interview “how do you convince your teammates that you’re right?” I answered with probably the most surprising answer: “I don’t.”

+

It’s taken me years to realize that being right isn’t terribly important. Especially when there’s more than one right answer — which there usually is. It’s more important to work as a team. It’s more important to be respected and to have respect for others.

+

A few weeks after that interview, I was pair programming with the person who had asked me the question. We’re both senior developers, so we both have a lot of experience and opinions based on that experience. We were writing a shell script, and we both had an idea in mind about how to write the code. I let him proceed with his idea. It was a pretty good idea; probably better than mine. But we paired very effectively. I’d let him finish some code, and I’d find a way to make it better. Then he’d find a way to make my code better. What we ended up with was so much better than my original idea, and his original idea as well. I commented on this, and he agreed. We left that pairing session feeling the high of having accomplished something rare — two very experience programmers coming up with better code than we could have imagined going in.

+

I like to think of writing code as a path towards a destination. The destination is good code that does what it’s supposed to, is readable (intention revealing), is concise, and is well-factored so as to be easy to change in the future. But there are many paths to that destination. Especially when pair programming, the code gets refined as ideas are shared, getting you closer and closer to that destination. Sometimes the different paths end up with the same code; sometimes they end up with different code. But if the code does its job well (according to those criteria), it doesn’t really matter what code you ended up with.

+

Thinking back to the original question, I think the answer is a little more nuanced. (I’m pretty sure I followed up with an explanation, but I don’t recall the details.) It truly doesn’t matter in a lot of cases. If there are different paths that lead to the same destination, and they’re all roughly equal, then my rule works. It can also work even if the first path you try (a “wrong” path) doesn’t work out, as long as it’s easy enough to try a different path. I find that the majority of day-to-day coding fits these conditions.

+

But there are some places where it’s costly to get things wrong the first time. In these cases, it’s worth spending some time thinking about and discussing the alternatives before getting started. Software architecture is the big one that comes to mind. In general, weighing the pros and cons of each option and applying previous experience works best.

+

So next time you think about trying to convince someone you’re right, fight the urge. Instead, let them show you what they’re thinking. They just might surprise you. And more importantly, you might surprise yourself.

+]]>
+ http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right/feed + 0 +
+ + My First Open Space + http://blog.boochtek.com/2016/02/10/first-open-space + http://blog.boochtek.com/2016/02/10/first-open-space#respond + Thu, 11 Feb 2016 05:42:54 +0000 + + + + http://blog.boochtek.com/?p=307 + Continue reading "My First Open Space"]]> + I recently attended an Open Space hosted at work. I’d never been to an Open Space, and didn’t know what to expect. We’d been told that this was a workshop to help Engineering Managers (my role), Product Owners, and Product Analysts find better ways of working together. But due to the way an Open Space works, it evolved into something completely different — and better.

+

We’d brought in Diana Larsen to facilitate the Open Space. Diana is a stalwart of the Agile community, focusing on how people and teams interact. She literally (co-)wrote the book on Agile retrospectives. Diana was also kind enough to be our guest at an Agile LINC meetup later in the evening.

+

The morning started off with all the participants sitting in chairs arranged in a circle. Diana rang a chime, a nice soothing tone that literally set the tone for the day. (Most people didn’t seem to like it, but I thought it had its purpose.) It also acted as a call to gather in the meeting space. She then walked around the inside of the circle as she explained what was going to happen.

+

The idea behind Open Spaces came with the realization that at a conference, the “hallway track” (ad hoc discussions in the hallways) was often more valuable than the scheduled talks. So they figured out a way to capture that experience. There are only a few rules:

+
    +
  • Whoever shows up is the right group
  • +
  • Whatever happens is the only thing that could have
  • +
  • Be prepared to be surprised
  • +
  • Whenever it starts is the right time
  • +
  • When it’s over, it’s over
  • +
  • The Law of 2 Feet: If you’re not learning or contributing, go somewhere else
  • +
+

Once Diana set the scene and explained the rules, people came up and presented ideas for topics. If you proposed a topic, you chose a time and location on the topic board. At that time, you’d facilitate a discussion on that topic. The format was really conducive to discussions. There was no preparation, so discussions were from the heart — lots of people felt that they could contribute.

+

Being new to the company (only 2 months), I got a lot out of the workshop, beyond the content itself. I got to get to know several more people. I’d even say I made a few friends. Having open discussions, we found that many of the teams were having the same issues. This made it easy for me to talk to other people.

+

All-in-all, I found the Open Space to be extremely conducive to discussions aimed at identifying issues, brainstorming solutions, and planning action items. I felt like we made great strides in addressing some of the biggest problems our organization is currently facing.

+]]>
+ http://blog.boochtek.com/2016/02/10/first-open-space/feed + 0 +
+ + Team Values + http://blog.boochtek.com/2016/01/04/team-values + http://blog.boochtek.com/2016/01/04/team-values#respond + Tue, 05 Jan 2016 04:56:46 +0000 + + + + + http://blog.boochtek.com/?p=294 + Continue reading "Team Values"]]> + I held a retrospective with my new team last week. The team includes 2 senior developers, 2 junior developers, a product owner, and a product analyst. I’ve joined the team as an engineering manager, which I think of more as a team lead with an elevated title. Being new to this group, I wanted a way to understand their values. What motivates them? What common values do we share that we can leverage to move forward in the same direction?

+

I started out with a pretty simple question: “What do you value (in regards to what we’re building); what are you willing to fight for?” I asked them each to write down several values and then put them on the board before looking at everyone else’s answers. I also asked each person to rank their values in order of importance.

+

It turned out that my question was a little vague. Some people thought about the question in terms of the end result, and some in terms of the process of creating the software. In some ways that was a bit of a problem, because the different interpretations led people in different directions. But in other ways, not pushing them in any particular direction got more varied answers, exposing how they think about the project and the product.

+

My list (in order) was Effectiveness (doing the right thing), Quality (doing things right the first time), Happiness, Purpose, and Teamwork. In retrospect, I should have put Happiness first. If I’m not happy at work, I don’t really want to be there, and need to move on. (Sometimes I can trade some happiness at work for more happiness at home, but I’m definitely a person that needs to be happy at work.) The other values serve to improve my happiness, but the happiness is more important.

+

My own issue ranking my values turned out to be a problem with the ranking in general. Should they be ranked in importance of the necessity of the value as an outcome, or in importance of the necessity to focus on the value? I think the former is what I was looking for, but even I wasn’t clear on that when I began the exercise.

+

After everyone put their values up on the board, we read them off. Then I asked the team to choose several values that we share as a team. We pulled them off the individual members’ lists, and put them in the team list. Then I asked each person to come up and rank those values, then explain why they had ordered them that way, especially when they ordered them significantly different than the last person. I was hoping to come to some convergence of the rank over time, so we could document our values in rank order. That didn’t happen. But the discussion was illuminating to me and to everyone on the team.

+

I think the most interesting part about the lack of convergence was the difference between the developers and the product guys. The product guys definitely viewed the values more in terms of outcomes than the process. That makes sense — they’re not as intimately involved in the process of building the product.

+

We were able to converge on the top priority though: Will the user buy the product? This was a combination of a couple different values that we merged together. This included the end user experience as well as making sure the team would continue to have a reason for existing. The rest of the values we left unordered: Teamwork (cohesiveness), Data driven decisions, Team ownership, Simplicity, Effectiveness, Maintainability / Supportability, Quality, Automation, and Performance. I think that’s a pretty decent list.

+

As a couple teammates pointed out, those values are probably in part a reflection of this current point in time. If I asked the same question some other time, under different conditions and team dynamics, the answers would probably change a bit. And we’d probably come up with other answers if asked again, just due to randomness of the way we think about these things.

+

But I don’t think I’d do this activity a second time with the team. It was really about understanding our motivations — both our own, and those of our teammates. I found it effective in that way, and also in helping the team to think about our culture and how we can work to shape it to help us all push in the same direction.

+

There are a few caveats. When I asked for feedback on the exercise, one teammate pointed out that it wouldn’t work if people weren’t honest about their values, and they answered with what they thought management or their teammates wanted to hear. I don’t think that was an issue with this group, but it’s something to keep in mind.

+

The other major thing I’d do is to make it clear up front that I’m not looking for any action items from this activity; it’s more about understanding each other and ourselves. And next time, I’ll work to clarify how to rank the values.

+

I would recommend this activity for a new team, or when the makeup of the team is changing in some significant way. I wish there was a way to help a team converge on the ranking of their values, but I suppose I should be happy that agreeing on the set of important values went pretty quickly. And the diversity of ideas and opinions is probably a blessing that I should be embracing — the more ideas we have, the wider the variety of solutions we can imagine.

+]]>
+ http://blog.boochtek.com/2016/01/04/team-values/feed + 0 +
+ + 2015 Year in Review + http://blog.boochtek.com/2015/12/28/year-in-review-2015 + http://blog.boochtek.com/2015/12/28/year-in-review-2015#respond + Tue, 29 Dec 2015 05:28:38 +0000 + + + + + + + + http://blog.boochtek.com/?p=287 + Continue reading "2015 Year in Review"]]> + It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+]]>
+ http://blog.boochtek.com/2015/12/28/year-in-review-2015/feed + 0 +
+ + Face Your Fears + http://blog.boochtek.com/2015/12/21/face-your-fears + http://blog.boochtek.com/2015/12/21/face-your-fears#respond + Tue, 22 Dec 2015 05:38:29 +0000 + + + + http://blog.boochtek.com/?p=285 + Continue reading "Face Your Fears"]]> + I’ve always been someone who faces my fears.

+

I have a moderate case of arachnophobia. I don’t run away when I see a spider, but it creeps my out when one is crawling on me. When I was in college, I decided to buy a tarantula to attempt to get over my irrational fear of spiders. I thought I’d be able to get more comfortable with the tarantula over time, eventually to the point of letting it crawl on my arm. It didn’t work. Although I did find that my fear of tarantulas is rational — I got a terrible case of hives just from touching the urticating hairs that fell off into its water sponge.

+

Last week, I was on vacation in Mexico. One of the excursions we took involved jumping in the water a lot. I’m not a strong swimmer — mostly because I have a hard time closing my nose; I hold my nose when I jump in. We zip-lined into the water a lot. At one point, there was a cliff to dive into the water from. It was about 15 feet above the water. It didn’t look so far down before jumping. But it felt like a really long way down the first time I jumped from it. It was pretty scary for me. So I did it a second time. There wasn’t any peer pressure to jump a second time. I literally jumped a second time specifically because I was scared.

+

Fear is a weird thing. Fear is there to protect us. But it’s there to protect us from the dangers of the African savannah. Most of the things our fears protect us from don’t exist in our everyday modern lives. So maybe we should work to gain a better understanding of how our fears work, to figure out when to pay attention to them and when to ignore them.

+

Fortunately, our brain has a good mechanism to help us do this. Our brains basically have 2 main processing systems. The first one is for quick reactions. This one involves things that are nearly reflexes. Fear is in this system. The second system is our analytical reasoning system. This system takes longer to process, but is able to take on more information.

+

Whenever the situation allows us time for both systems to work, we need to listen to them both. We need to listen to our fears, because they’re there for a reason. But that reason might not pertain to our situation. So we need to realize that, and let the slow analytical system determine if we should ignore our fears.

+

If we don’t allow both systems to work, we’re not taking full advantage of our brains; we’re not taking full advantage of the situations that life is presenting to us.

+]]>
+ http://blog.boochtek.com/2015/12/21/face-your-fears/feed + 0 +
+ + Encouragement + http://blog.boochtek.com/2015/12/15/encouragement + http://blog.boochtek.com/2015/12/15/encouragement#respond + Wed, 16 Dec 2015 05:00:22 +0000 + + + + http://blog.boochtek.com/?p=282 + Continue reading "Encouragement"]]> + I’ve been on vacation the past week, in Cozumel, Mexico. One day, we went on an excursion called Xenotes. A cenote (say-NO-tay) is a sinkhole filled with fresh water. (The “X” is to give it a more Mayan-sounding trademarkable name.) We had a lot of fun swimming, kayaking, and zip-lining. A bilingual tour guide led our group, which consisted of people from across the US and South America, of various ages and physical abilities.

+

Our tour guide was a lot of fun. He made jokes, told us about the cenotes, and led us in the activities. But he also encouraged us. When someone was scared to do something, he was supportive. He told us that it was okay, and we could do it. But we also felt like it was OK to fail, if we really couldn’t. It really felt like his encouragement was literally creating courage.

+

What was really neat was that despite the language barrier, everyone else was also supportive and encouraging. Everyone cheered with encouragement before someone would attempt something difficult. And we’d cheer especially loud once someone accomplished something that was difficult for their abilities.

+

It was an awesome feeling to feel so supported. It made me feel like I was in a safe place, where I could try new things that were a little past my comfort zone. I was able to do the zip-line upside-down. I jumped off a 15-foot cliff into the water. I even jumped off the cliff a second time, even though I was a little scared.

+

Back at the resort, we played some volleyball in the pool. It was a similar situation, with players of varying ages and abilities. Again, we tried to help the weaker players feel comfortable so they could improve without feeling judged or self-conscious. It helped everyone have a good time. It made everything more fun to be in such a supportive environment, whether I was in a position as one of the more skilled (volleyball), or one of the less skilled (jumping or zip-lining into the water). We were all able to accomplish more, with less effort.

+

These experiences provide a good lesson that can be applied in a lot of places. Such a supportive environment would help any relationship, and any team. I’ve been on a couple really good software development teams, but I don’t think any of them have been as supportive as these two groups of strangers.

+

I’ve decided that this should be one of my goals as a team leader. I want to create an encouraging environment for the whole team. I want to make sure that everyone is comfortable enough that they feel like they can try things that are difficult, even if they might fail (as long as nobody gets hurt).

+

If a group of strangers can do this, so can your team. So can you and your significant others. We need to work every day to make sure that we’re supporting each other. It’s the best way to get everyone to achieve more.

+]]>
+ http://blog.boochtek.com/2015/12/15/encouragement/feed + 0 +
+ + Show and Tell + http://blog.boochtek.com/2015/11/24/show-and-tell + http://blog.boochtek.com/2015/11/24/show-and-tell#respond + Wed, 25 Nov 2015 05:21:06 +0000 + + + + http://blog.boochtek.com/?p=271 + Continue reading "Show and Tell"]]> + I’m wrapping up my current consulting gig at Mercy in December, and starting a new gig at CenturyLink. I’m a software developer, but that’s only a part of what I do. What I really do is join a team and help them improve the way they work — both their processes and their technical skills.

+

I think this is a key differentiator for me as a consultant. Most consultants (and Agile coaches) come in and tell people what to do. I don’t like to just tell people what to do. I’d much prefer to work side-by-side with them, getting a better understanding of what their challenges are. Once I have a better understanding of the challenges, I’m able to better brainstorm some ideas to try. Then we can experiment to see what will work and what won’t.

+

Instead of telling people what to do, I show them how. Most people learn better from seeing than from hearing. They also learn better if you explain how and why, not just what. So showing them how to do something is more effective than telling them. By showing and doing, you can also set a good example. This is especially important when collaboration is a large part of what needs to be improved.

+

I’ve found that this style of consulting is more highly respected by everyone. I build trust with developers by working closely with them. Managers like to keep me around once they see how effective these methods can be, so the gigs I take on tend to last relatively long.

+

The biggest problem I have is explaining how this works. I don’t really know what to put on my résumé. Sometimes I call myself an Agile practitioner, and sometimes an Agile player/coach. But those aren’t terribly satisfying descriptions. I’m considering actually putting “I help teams improve the way they work — both their processes and their technical skills” on the résumé. But that seems awkward, and misses the show versus tell part. I’d be open to any suggestions.

+

 

+]]>
+ http://blog.boochtek.com/2015/11/24/show-and-tell/feed + 0 +
+ + Happiness Retrospective + http://blog.boochtek.com/2015/11/09/happiness-retrospective + http://blog.boochtek.com/2015/11/09/happiness-retrospective#respond + Tue, 10 Nov 2015 05:56:58 +0000 + + + + + http://blog.boochtek.com/?p=268 + Continue reading "Happiness Retrospective"]]> + I facilitated a retrospective today; it was one of the best retros I’ve ever been involved with. I figured out what activities I wanted to do earlier in the morning. They were really quite simple. I wanted to focus on happiness.

+

How happy are you at work?

+

I started with two questions that I’ve used with teams before, to some success (although not so successful for one particular team). The first question I asked was “How happy are you at work?” I had them put a rating from 0 to 10, with 0 meaning they should have quit last week, and 10 meaning they couldn’t imaging being happier at work.

+

The answers were mostly 7s and 8s, with a 5 and a 9. The average came to 7.5, which is pretty good. The 5 concerns me a bit, especially that it’s 2 points lower than anyone else’s answer.

+

How effective do you think the teams is?

+

The next question I asked was “How effective do you think the teams is?”. Again, from 0 to 10, with 0 meaning they can’t accomplish anything, and 10 meaning you couldn’t imagine a more effective team.

+

When I ask both of these questions, the scores are always highly correlated. If your team isn’t doing good work, it’ll make you unhappy. And if you are unhappy, you’re less likely to do your best work. This team was no different; the 5 and 9 became a 6 and a 10, and most of the 7s became 8s, for an average of just under 8.

+

What makes you happy at work?

+

The next question I asked was “What makes you happy at work?”. The answers were mostly about the teamwork and teammates. This went quicker than I expected. At this point, I was worried the retro would only last a little more than 30 minutes.

+

What would make you happier at work?

+

The final question I asked was “What would make you happier at work?”. This was the real pay-off. We spent about half an hour just talking about the things that would make us happier, and what we could do to improve our happiness. We came up with 10 potential action items. I usually limit teams to trying 3 or 4 action items, but most of the items are quite small, so we’re going to try 6 of them. One is just observing another team’s standup meetings, to see how they’re using their time effectively.

+

Everyone went away feeling that this was a really good retro. It felt good to focus on happiness. Happiness is something I’ve been talking a lot about on the This Agile Life podcast, and it felt good to take some action on it. I’ve done “positive-only” retros before, but this one felt even better than that, by specifically targeting happiness and how we can achieve it.

+]]>
+ http://blog.boochtek.com/2015/11/09/happiness-retrospective/feed + 0 +
+ + Impromptu Retrospective + http://blog.boochtek.com/2015/11/02/impromptu-retrospective + http://blog.boochtek.com/2015/11/02/impromptu-retrospective#respond + Tue, 03 Nov 2015 03:44:44 +0000 + + + + + http://blog.boochtek.com/?p=265 + Continue reading "Impromptu Retrospective"]]> + I’m surprised that I haven’t gotten this story down in print before. It’s something I’ve mentioned many times — including a few times on the podcast. It’s a great story about the power of retrospectives, and it’s a great story about the power of a blameless post-mortem.

+

I don’t recall all the specifics at this point. It was about 5 years ago. I’d just noticed that Arun had made some sort of mistake. That’s fine, people make mistakes. The thing that was different about his mistake was that I had made the same mistake about a week prior. And Amos had made the same mistake about a week before that.

+

Noticing a pattern of mistakes, Amos and I called an impromptu retrospective. We gathered all the developers into a conference room. We explained the problem that we were running into. At first, Arun was defensive. That’s understandable; he thought we were there to come down on him, to lay blame. But we made it clear that we weren’t focusing on him. We admitted that we had also made the same mistake recently. We weren’t there to lay blame; we were there to figure out how our team could stop making the mistake. It took Arun a few minutes to get over the defensiveness.

+

With the defensiveness out of the way, we could focus on the issue at hand. We were able to figure out the root cause of us all making the mistake. (I don’t know if we played the “5 whys” game, but I’m sure we effectively did something similar.) And with that, we were able to change our process, so that nobody else would make the same mistake again.

+

There are 2 important points to this story. First, you don’t have to wait until a scheduled retrospective to hold a retrospective. This one was impromptu, and it’s the best one we ever had. We saw a problem, addressed it, and found a solution in less than an hour. Had we waited until the end of the week, we would have forgotten some of the details, and wouldn’t have been as effective at solving the problem. Second, when addressing problems, take your ego out of the equation. If you’re in a position of authority, take the blame — but never place blame. Focus on what’s important — solving the problem.

+

And don’t forget the Retrospective Prime Directive:

+

Regardless of what we discover, we understand and truly believe that everyone did the best job they could, given what they knew at the time, their skills and abilities, the resources available, and the situation at hand.

+

 

+]]>
+ http://blog.boochtek.com/2015/11/02/impromptu-retrospective/feed + 0 +
+ + The Problem With Estimates + http://blog.boochtek.com/2015/09/28/no-estimates + http://blog.boochtek.com/2015/09/28/no-estimates#comments + Tue, 29 Sep 2015 03:39:41 +0000 + + + + + http://blog.boochtek.com/?p=261 + Continue reading "The Problem With Estimates"]]> + +
+

I’m a big proponent of Agile (mostly XP; I’m mostly anti-Scrum) and I’ve contributed some to the #noestimates “movement”.

+

I don’t really mean that nobody should ever estimate anything. I mean that I’ve never seen useful (fine-grained) estimates anywhere. Here are some of the problems with estimates that I’ve seen frequently:

+
    +
  1. We’re not good at estimating how long things will take. We’re usually optimistic about how quickly we can get things done, and almost always miss thinking about things that will take more time. I’ve never seen a case where a project is completed more quickly than estimated. I’ve only rarely seen fine-grained (story-level) tasks completed more quickly than estimated.
  2. +
  3. Management asks for estimates and then treats them as deadlines. The team then learns to inflate their estimates. Then management learns to reduce the estimates they’re given. Given fudge factors in each direction, the estimate no longer has much reliability. Even if you’re using story points, the point inflation/deflation leads to less consistency and therefore reduced reliability.
  4. +
  5. Estimates that are given are negotiated down, or simply reduced. This leads to the question why you’d ask for an estimate and not take the answer provided. If you’re not going to listen to the answer, why are you asking the question? This is probably the craziest one on the list — given my first point, increasing an estimate would make sense. Reducing the estimates is just magical wishful thinking.
  6. +
  7. Plans change and work is added, but the deadline (presumably based on the estimates) is not changed to correspond with the extra work involved. So again, you’re not actually even using the estimates that were given.
  8. +
  9. Management dictates deadlines arbitrarily, without speaking to the people who will be doing the work. Spending time estimating how long each task will take when the deadline is already set is completely pointless.
  10. +
  11. Almost every deadline is complete bullshit, based on nothing. Often the excuse is that marketing needs to know when something will come out, so that they can let people know about it. Why they need to know the exact release date way in advance, I’ve never been able to figure out. Many people intuitively know that the deadlines are bullshit, and will likely be allowed to slip. The only exception to bullshit deadlines I’ve come across are regulatory deadlines. (I know there are a few other exceptions out there.)
  12. +
  13. Estimation at a fine-grained level isn’t necessary. Many Agile teams estimate using story points, and determine a conversion from story points to time based on previous empirical data. This is fine, except that the time spent estimating the story is wasted time — counting the number of stories almost always gives the same predictive power. Teams tend to get better at breaking up stories over time, so that they’re more consistent in size, so this becomes more likely over time.
  14. +
  15. The ultimate purpose of an estimate is to evaluate whether the proposed work will be profitable, and therefore worth doing. Or to compare the ROI (return on investment) between alternative projects. But to know that, you’ll have to know what value that work will provide. I don’t believe I’ve ever seen that done — at least not at a fine-grained level. Usually by the time you’re asked to estimate, the project has already gotten approval to proceed.
  16. +
+

I’ll note that most of these pit management against the team, instead of working together toward a common cause. Most of the practices also lead to seriously demoralizing the team. And most of the time, the estimates aren’t really even taken into account very much.

+

My advice is to first understand the value of a project before you consider estimating the costs. The estimation at this point will be very rough, so make sure that you have a very wide margin between the expected value and the rough estimate of the cost. If you’re pretty certain of the expected value, I’d probably want to make sure I could still be profitable even if it took 3 or 4 times as long to complete as the rough estimate. And if there’s uncertainty in the expected value, much more.

+

Another way to mitigate the risk of throwing money at something that’s not going to have positive ROI is to reduce the feedback loop. Order the work so that the tasks are ranked in order of value to the customer. (Realistically, you’ll have dependencies of tasks to worry about, and should consider effort involved too.) So work on the most valuable feature first — get that out into production as soon as possible. Once that’s done, you can assess if your ROI is positive or not. Keep iterating in this fashion, working on the features that will provide the most value first. Keep assessing your ROI, and stop when the ROI is no longer worth it, compared to other projects the team could be working on.

+

At a fine-grained level, if you’re using story points, I’d ask you to do the math to see if just counting the stories would be as effective at predicting how much will be done over time as using the story points. If so, you can save the time the team spends on estimating stories. I’d still recommend spending time talking about stories so that everyone has a shared understanding of what needs to be done, and to break stories up into a smaller, more manageable size — with one acceptance criteria per story. Also take a look to see if empirical average cycle time (how long it takes a single story to move from start to finish) might provide you the predictive power just as well as estimates. (I.e. is it bandwidth or latency that really provides the predictive power you’re looking for?)

+

And don’t forget Hofstadter’s Law: It always takes longer than you expect, even when you take into account Hofstadter’s Law.

+
+ +
+
+
+]]>
+ http://blog.boochtek.com/2015/09/28/no-estimates/feed + 2 +
+
+
diff --git a/public/category/agile/page/2.html b/public/category/agile/page/2.html new file mode 100644 index 0000000..7445376 --- /dev/null +++ b/public/category/agile/page/2.html @@ -0,0 +1,487 @@ + + + + + + + + + + + + + + + +Agile – Page 2 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

From Agile To Happiness

+ + + +
+

The Agile Manifesto was written in 2001, as a way to explain the common values amongst several “light-weight” software development methodologies that had come about. The term “Agile” was chosen as a shorthand for those commonalities.

+

Once “Agile” started to show success, we started to see many people use the term to market their products and services, whether or not they really believed in the values and principles of the Agile Manifesto. It’s gotten to the point where some of us don’t see much value in using the term “Agile” any more. Even some of those involved in creating the manifesto have suggested new terms. Dave Thomas suggests “Agility” and Andy Hunt has started working on something called GROWS. Personally, I’m considering going back to the term “Extreme Programming”, even though I’ve incorporated ideas from other Agile methodologies.

+

It recently occurred to me that Agile, when done “right”, is closely aligned with the happiness of the team members. This is really interesting, because it aligns the interests of the employees and the business — a win-win situation. My next thought was that maybe the next step after “Agile” will be a focus on happiness and motivation.

+

I’ve recently been thinking about personal motivation lately, in the context of team practices. According to Daniel Pink’s book Drive, people are motivated by autonomy, mastery, and purpose. I personally add a fourth that can sometimes trump the other three: identity. And of course, happiness can also be motivating — both in the attempt to achieve happiness and in just being happy. (I suspect that happiness is more of a parallel to motivation than a cause though.)

+

There are a couple different ways that happiness can be achieved at work. The traditional way is for work to be a means to an end. In this view, the purpose of your job is to provide the money to live your life (outside of work) the way that you want to live it. There’s nothing wrong with this way of thinking. But for the lucky few, we can work on something that makes us happy in and of itself. That’s generally done by finding a job doing something that we enjoy.

+

But perhaps that’s thinking about things the wrong way. For one, plenty of people who have gone that route are still unhappy at work. I think a lot of that has to do more with the context surrounding the work than the work itself. Maybe you’ve got a lousy manager. Maybe you don’t like the people you work with. Maybe the tools you have to work with are bad. Maybe the processes add a lot of unnecessary tedium.

+

So maybe we need to find ways to be happier at work. Most of the Agile practices seem to make team members happy. For example, replacing a light-weight process for a heavier process always makes me happy. And I typically leave retrospectives in a good mood. So that’s a good starting point. But we should see if we can take the idea further. If we take employee happiness as a core value, where can we go? What kind of practices would we want to add? Please share any ideas in the comments below.

+
+ + +
+ +
+
+ +

When Should We Do TDD?

+ + + +
+

On a recent episode (78) of This Agile Life, my fellow hosts talked about when to do Test-Driven Development (TDD). They all said that you should always do TDD — at least for anything that will go into production; there’s an exception for experimenting or “spiking”.

+

I wasn’t on that episode, but later commented on the topic. (Episode 83 — which wasn’t really all that great.) My take was slightly different. I said that you should do TDD only when the benefits outweigh the costs. Unfortunately, we usually greatly underestimate the benefits. And the costs often seem high at first, because it takes some time to get good at writing automated tests. Not to mention that both the costs and the benefits are usually hard to measure.

+

What is the cost of writing automated tests? I’ve asked this question before and recorded the answers (inasmuch as we have them) in a previous blog entry. TDD costs us about 10-30% in short-term productivity. The benefit that they found was a reduction in bugs by 30-90%, and a decrease in code complexity by about 30%.

+

But what about when the costs are higher than the normal 10 to 30 percent? One good example of this is when there’s no test framework for the situation you’re testing. This might be a new language or framework that you’re working with. More likely it’s a complex API that you have to mock out. So that could increase the cost of automated testing so as to outweigh the benefits — especially on a short project. I could imagine situations where the cost of writing the mocks could eat up more than the project itself.

+

Another case where we might consider skipping testing is when we’re more concerned about time to market than quality. This is almost always a mistake. Your code will almost always last longer than you expect. (Remember Y2K?) And if the code lasts longer than you expect, that means you’ll have to deal with bugs that whole time. But we have to work with the information we have at the time we make our decisions. And sometimes that might tell us that time to market is more important than anything.

+

One final case I can imagine is when a true expert is coding something that they’re very familiar with. I could picture someone like Uncle Bob writing code (in a language that he’s familiar with) without tests just as effectively as I could write code with tests.

+

But these situations should not be common; they’re edge cases. In almost all real-world cases, TDD is the right thing to do. Don’t forget, TDD is also a design discipline — it helps you design a better API. So keep doing TDD. But as with any practice, don’t do it blindly without considering why we’re doing it. Make sure you understand the costs, and whether the benefits outweigh the costs.

+
+ + +
+ +
+
+ +

Good Enough

+ + + +
+

I ran into some former colleagues recently, from a company where I had worked to help transform the team to be more Agile. They’ve gone through some reorganization and management changes recently. One of the guys said that their team culture has helped them maintain quality in the face of those changes. This struck me as odd, since I had considered the work I had done there as somewhat of a disappointment. While I felt I had made a small dent, I didn’t feel like I’d made a true Agile transformation. Much of what I had taught didn’t seem to “stick”.

+

Later that night I thought about why my opinion of the team and his opinion were so different. There are a lot of reasons why an Agile “transformation” could fail. It could be due to lack of support from management. Or even worse, not getting the team members to buy into the ideas — usually due to fear of change. But while those had some effect in this case, they weren’t really the main issues. Now that I’ve had some time and some distance from that team, I’ve gained some clarity on what the real issues were.

+

I think the reason that I think this transformation was a failure was due to the continued pace of change that true Agile requires. Basically the team experienced “change fatigue”, where everything keeps changing and you feel like you can never catch your breath. But the more interesting thing is how our perceptions differed. He seemed to think that the transformation was a success — they’re doing things better than they used to. I view it as more of a failure, because they basically stopped improving — at least at the pace that I was hoping for.

+

I think this difference in mindset is pretty fundamental. My view of Agile involves continuous improvement — always inspecting and adapting our processes. I suppose that means that my preferred flavor of Agile is “Kaizen“. But other people don’t see improvement and change as a constant — they see each change as a means to an end. I’m starting to realize that neither viewpoint is objectively correct. Maybe they’re both fine viewpoints to have. Maybe I shouldn’t be so hard on myself and view that engagement as a failure, especially if my teammates view it as a success. Maybe perfection is the enemy of the good. And maybe I need to learn to be happy with “good enough”.

+

 

+
+ + +
+ +
+
+ +

The Power of 1%

+ + + +
+

I frequently participate in a podcast called This Agile Life. Recently, a listener asked how much time Agile teams should spend on self improvement. I said 10% to 25%, leaning towards 15% to 20% for most teams. That comes to at least one hour per day, and maybe even more than one day per week.

+

I’m including personal self improvement and team retrospectives in this self-improvement time. This can be as simple as configuring your IDE to make you more efficient, learning to use a tool better, or following up on action items from a retro.

+

That may seem like a lot of time “wasted”. But I think I can justify the cost of all that time.

+

The purpose of spending time on team or self-improvement — the whole point — is to increase our performance and our efficiency. How much improvement can we expect? Can we improve by 1% each week? That doesn’t sound too unreasonable. I think that’s an achievable goal for almost any team, at least on average.

+

Spending 20% of your time to gain 1% doesn’t seem like it’s worth it — until you consider the long term. With compound interest, you’ll be 67% more efficient by the end of a year.1 At that point, you’ll be able to get things done in 59% of the time — saving 41% of the time required at the beginning of the year.2 The following years will show even more progress, as compared to when you started. If 10x programmers exist, continuous improvement is apparently the way to get there.

+

So there’s a pretty good return on investment, even with a small amount of improvement each week. You’ll be significantly more efficient.

+

But efficiency isn’t really what you should aim for. You should aim for effectiveness. You can be efficient in creating the wrong thing. Part of improving should be ensuring that you’re not just building things right, but that you’re building the right things. Build what the customer really needs. Find ways to ask the right questions.

+

Most importantly, find ways to keep improving. It would be a waste of time not to.

+

1: (1.01 ^ 52) – 1
+2: (0.99 ^ 52)

+
+ + +
+ +
+
+ +

Resolutions

+ + + +
+

January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+
+ + +
+ +
+
+ +

TDD Is Alive And Well

+ + + +
+

I went to RailsConf this year, and the very first talk was a keynote by David Heinemeier Hansson (DHH), the creator of Ruby on Rails. The TL;DR of his talk was “TDD rarely has value”. He followed up with a blog post the next day, titled “TDD is dead. Long live testing.“, and 2 more posts. I think this line of thought is terribly misguided, and causing more harm than good. This article is my response.

+

First, I would like to address the good points of the talk. He said that programming is pseudoscience, and that people want to tell us that there’s a secret to being a better programmer. But what it really takes is working hard — reading a lot of code, writing a lot of code, and rewriting a lot of code. He’s right. And I also agree with him that you should forget about patterns for a while when learning to code. Beginners try to throw patterns at a problem instead of letting the patterns emerge where they’re supposed to.

+

I don’t completely agree that programming is a pseudoscience. In some ways it is, but I think it’s more of a craft. It’s a craft, because there’s a lot of science involved, but there’s also an art to doing it well. And like any craft, you’re always working to get better. So to respond to DHH’s stance that “software is more like poetry than physics”, I think it falls
+somewhere in between.

+

With regard to the software engineering practices we use, there really isn’t much science available, mostly because it’s a soft science. That is, it’s really hard to isolate a single variable when comparing code between projects. And nobody has the time or money to write the same code so many times that the differences would be statistically significant.

+

So we don’t have much science on TDD. But we do have some. Here’s a collection of several: StudiesOfTestDrivenDevelopment. And here’s one that explicitly looks are the difference between test-first and test-last: Does Test-Driven Development Really Improve Software Design Quality? What do these tell us? They tell us that TDD costs us about 10-30% in short-term productivity; reduces bugs by 30-90%, and decreases code complexity by about 30%. As Code Complete tells us (in section 20.5, with studies to back it up), improving quality reduces development costs. So, like most Agile practices, this is a case where spending a bit more time in the short term leads to time savings in the long term.

+

The more important lesson in the talk was that you have to do what works best for you and your situation. If TDD doesn’t give better results, then either find out how to make it give better results, or stop using it. As we often say in the Agile world, Agile doesn’t mean that you can stop using your brain. While I think TDD is appropriate in most situations, there are cases where it’s not worth the additional up-front cost. If the most important thing for your project is time-to-market, then not testing might be the right decision for you.

+

To me, TDD provides a bunch of benefits. First and foremost, TDD is a design discipline. It ensures that I think about how my code will be used before I think about how to implement it. This is very powerful in ensuring that the code is well-written from the perspective of other code using it.

+

Tested code provides confidence to be able to make changes without breaking things. If we write tests after the code, we’re less likely to write them. Tests written after the code also tend to test the implementation instead of the desired functionality. What we really want is tests written as a specification. With tests as a specification, we can come back later and understand why code was written. Without tests, or with poor tests, we can’t understand why the code is there; if we want to rewrite it, we don’t have the confidence that we’re not missing something. Writing tests first also ensures that we only write the code that is needed to implement the required functionality.

+

I’m not sure why DHH hasn’t “gotten” TDD. I’m not sure if it’s because he’s  a better coder than average, or if he just thinks in a different way than most of us. I think it’s partly because he doesn’t understand TDD, which he admitted might be the case. And I think he’s conflating TDD and unit testing.

+

DHH is influential in the developer community, especially those newer to Ruby and Rails. People listen to what he has to say. I was happy to see almost every other speaker made fun of DHH’s ideas, and most of the crowd knew better. But there will be a lot of others who will hear DHH, respect his opinions, and not give TDD the try that it deserves. And that’s sad, because it will lead to an overall reduction in code quality in the world.

+

Here are some other people’s thoughts on the matter:

+ +

 

+
+ + +
+ +
+
+ +

Estimation Isn’t Agile

+ + + +
+

I don’t believe that estimation should be part of any Agile practice.

+

One of our managers recently mentioned that we hadn’t met the “contract” that we had “committed” to in our last iteration. This was complete nonsense, because A) we hadn’t made any such commitments, and B) we completed many more story points than the previous iterations (and without inflating story points).

+

estimates-as-deadlines

+

But her language made me come to several realizations. First and foremost, estimates are contracts. Sure, they’re not supposed to be treated as commitments, but they almost always are. And what does the Agile Manifesto say about this? It says that we should value customer collaboration over contract negotiation, and responding to change over following a plan. So it’s pretty clear that treating estimates as commitments is completely counter to the Agile values.

+

Why does this matter? What benefits do the Agile values bring us? I think the biggest benefit they bring is changing the way that we work, so that we can better deliver value to our customers. Without Agile, we’d just keep working the way we’ve always done things. And that didn’t seem to be working out so well. If we follow the Agile values and principles, at least we’ll have a fighting chance of improving our ability to deliver value.

+

Ask yourself — have you ever seen a software development project that was on time and on budget? Where the estimates were spot-on? Of course not. For one, we’re terrible at estimating. For another, our plans change — either from external factors, or from what we learn as we go.

+

Improved Estimation

+

To me, Agile is also about facing reality — and embracing it. It realizes that we’re terrible at estimating. It realizes that plans change. Most Agile methodologies have some tricks to counteract Hofstadter’s law. Generally, we use relative story points instead of hours, and then use an empirical factor to convert points to hours.

+

When this works, it is better than any other estimation I’ve ever seen. But it doesn’t work very often. People have trouble with relative estimation. How do you set the basis for what a point means without relating it to actual hours? Affinity estimation could work, but then you have to remember what the basis was. We’ve got a large distributed team, and when we tried this, we couldn’t all remember what the basis was.

+

Since we couldn’t get affinity estimation to work, we tried changing to perfect hours (only powers of 2). But then people thought of them as time. When we took longer than the estimate on an individual story, managers and team members thought we were taking longer than we should have. So our estimates ended up causing problems.

+

What Can We Do Instead?

+

Managers want estimates so that they can have predictability. They want to know when new features will be available. Is there a better way to get what we need?

+

I believe there’s a better way — prioritization. If you work on the most important thing first, then the most important thing will get done first. We should always be working on the next most important thing.

+

What if there’s more than 1 thing that’s most important? Then you’ve failed. You’ve failed at logic if you can’t understand that only 1 thing can be most important. You’ve failed at prioritizing the customers’ needs. You’ve failed at project management.

+

Arguments

+

1. Why can’t you just tell us how long it will really take?

+

Because we don’t know. Because we can’t know. This is the first time we’ve ever implemented the functionality you’ve asked for. If we’d done it before, we’d just use that existing code. As Glenn Vanderburg pointed out in his excellent talk on Software Engineering, we’re not building software, we’re architecting it.

+

2. But we have to tell our customers what to expect.

+

Why? Is the product so bad that you can’t keep customers around without leading them on with future enhancements? And why do customers need exact dates? A general roadmap telling them what the priorities for upcoming features should be sufficient.

+

3. But we have to have messaging about new features.

+

OK. Then send out that messaging once the feature has made it to Staging. Or even after it’s been rolled out to Production.

+

4. But we’ve promised these new features to the customers by this date.

+

Ah, so you’ve made promises to the customer that you don’t have control over. Have you ever heard of “under-promise and over-deliver”? That’s how you create happy customers. Yet you’ve done just the opposite, haven’t you? And then you want to blame someone else.

+

Risk

+

Estimates are risk. But the risk doesn’t come at the end, when the estimates are shown to be incorrect. The risk was in asking for the estimates in the first place, and placing trust in them. Don’t do it. Don’t promise things that you can’t be sure of.

+

Embrace this reality. Embrace this uncertainty. Always focus on what’s most important. That’s how you make customers happy.

+
+ + +
+ +
+
+ +

Slow Down!

+ + + +
+

There’s a tweet that I saw recently, with some simple advice for novice programmers:

+

Slow down.

+

This is probably good advice for most programmers. Our team recently noticed that every time we try to rush things, we make mistakes. And the mistakes end up costing us more time than if we had just done things at our normal pace. Slowing down ensures that you do things right, and when you do things right, you end up with a higher-quality product.

+

Speed and Code Quality

+

There are 2 types of code quality: internal and external. External code quality can be measured by how many bugs have been reported by customers. Internal code quality is harder to measure, but it mainly deals with the ability to change the code. When your internal quality is low, you’ve got lots of technical debt, and it’s harder to make changes.

+

So when you try to write code quickly, code quality decreases, leading to a code base that takes more time to make changes to. Conversely, when you slow down, your code quality improves, and it becomes easier to make changes more quickly. So when writing code, slowing down in the short run leads to a speed-up in the long run.

+

Speed and Process Improvement

+

But writing code isn’t the only place where we try to speed up. On an Agile team, we’re always trying to improve the way we work — especially at the beginning stages of an Agile transformation. So we’re eager to make changes in our processes. But I’d urge you to slow down here as well.

+

My colleague Amos and I frequently argue over pair switching. It’s funny, because we agree on everything except for 1 small detail. We both think pair switching is very important, to ensure that team members see more of what’s going on, to bring more ideas to each story, to prevent knowledge silos, and to encourage team ownership. Where we disagree is how long an ideal pairing session should last. I think pairs should switch every 2 hours, and he thinks 1 hour is ideal. I’ve seen teams reach the 1 hour pairing sessions successfully. But usually not without some pain and even often failing at the first attempt.

+

There’s nothing inherently wrong with failing. But if you fail at something, you’re not likely to try again. After all, you should learn from your failures, right?

+

So if you want your team to do something, you probably don’t want them to fail at it. If they fail, they won’t want to try a second time. That’s just human nature, and learning from failure. While you might think that they failed because they weren’t ready for the change yet, they’ll most likely think that they failed because this particular change won’t work for their situation. And they probably won’t know what to change when trying again, so they won’t try again.

+

I’ve seen this over and over. Back when Linux was up-and-coming, when a consultant pushed a company into using Linux before they were ready for it, and it didn’t work out, that company was cautious about trying again. So instead of being on the leading edge of using Linux, or even the middle of the pack, they ended up more toward the trailing edge. Had they not been pushed, they would have gotten more benefit in the long run.

+

So my advice in process improvement is the same as in programming: slow down. Take small steps toward what you think is the ideal. Make a small change, see how it works out, and adjust. As long as you’re still moving in the right direction, I believe you’ll move faster by taking small steps than by trying to make big leaps.

+
+ + +
+ +
+
+ +

Empathy

+ + + +
+

I facilitated our team retrospective this morning. I felt like we made a little forward progress, but not as much as I would have liked. But it really brought one thing to the forefront of my thoughts today — empathy gained through communication.

+

We have a pretty large team by Agile standards — we had 20 people in our retro: 16 developers, 3 QA folks, and 1 manager. Out of those, only about 5 or 6 speak up regularly. I recently sent out a survey to the team, trying to get feedback on how we could improve our retros. A couple of the questions tried to get a feel for why people aren’t speaking up more. Only about half the people responded, and the answers didn’t really answer my question as well as I had hoped.

+

So on Amos‘s suggestion, we did the Safety Check exercise. We got a good set of answers to why people don’t feel safe. About half of the answers were about the fear of looking stupid in front of other people. About half of those mentioned the manager — they’re worried they might get in trouble for what they say. We talked some about fear and how it’s more often than not misplaced. And that the worst consequences are usually not as bad as you might think. But then we got to the crux of the problem — there’s not enough trust amongst the team, and especially between the team members and the manager.

+

About half of our team is new (within the past 6 months) — including the manager. While the developers have made some good progress building trust amongst each other, we haven’t had as much time with the manager to build trust between him and the rest of the team. So the lack of trust isn’t at all surprising.

+

Honestly, I already knew we had trust issues, and wanted to address them, but needed a way to lead the team to that same realization. With this exercise driving out the issue, we were then able to have a conversation about trust. The conversation was pretty good. We got more voices to contribute than probably any other topic we’d previously covered. (I was disappointed that the manager kept quiet though. I later found that he was trying to mitigate people’s fears by keeping quiet, but I urged him to contribute more in the future.)

+

But one point really stood out in my mind — a point of view that I hadn’t previously thought much about. Lauren, one of our QA analysts, pointed out that most human communication is non-verbal. We give tons of cues via body language, facial expressions, eye contact, tone of voice, even posture. I don’t recall if Lauren said it explicitly, but she pointed out that these cues build empathy between the speakers. She encouraged us to use more voice chat and video chat, as opposed to IRC text chat, because it would create more empathy between the people communicating, which would lead to more trust.

+

I spent most of the rest of the day talking to people on the phone or via Google Hangouts voice. And every single time, I consciously noticed that I was gaining empathy for the person I was speaking with. I assume (and hope) that that’s working both ways. I suppose that it’s always been happening, but I never really noticed it.

+

I’ve heard a lot of talk about empathy among Agile practitioners lately. It’s been mentioned on the Ruby Rogues podcast, and Angela Harms has been preaching it for years. I already understood how important it is. But until today, I didn’t really feel it happening.

+

So if you’re looking to build trust with someone, spend some time talking with them. Preferably in person, but if that’s not possible, seriously consider video or voice modes of communication, instead of sending an email or an instant message.

+

 

+
+ + +
+ +
+
+ +

Testing Rails Validators

+ + + +
+

It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/agile/page/3.html b/public/category/agile/page/3.html new file mode 100644 index 0000000..0f04a7a --- /dev/null +++ b/public/category/agile/page/3.html @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + +Agile – Page 3 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Introspective

+ + + +
+

Today I was informed that I’ve been impeding progress on our team.
+This was somewhat shocking, since I feel like I’m always pushing
+forward to make our team and myself better.

+

Like most anyone, my initial reaction to criticism was defensiveness.
+I don’t handle criticism well. (You might find that hard to believe,
+after reading the rest of this post. Maybe it’s just the confrontation
+part I don’t handle well.) Thankfully the blow was softened somewhat,
+because the person providing the criticism didn’t tell me directly —
+they told Amos, a trusted colleague of mine. Amos then passed it on to
+me. I’m grateful for that — this is the best way for me to have received
+that criticism.

+

What I did next is the astonishing thing. I swallowed my pride for a
+minute. Not an easy thing for me to do, for all the usual reasons, and
+more. I decided to address the problem head on. If someone was telling
+someone else that I was causing some sort of a problem, then even that
+perception was a problem that I needed to address. So I decided to hold
+a retrospective for myself. Retrospectives are the way Agile teams address
+problems and improve how they work. If it would work for a team, I figured
+it should work for an individual.

+

I’d held retrospectives by myself before. Before I even knew about
+retrospectives, I’d taken some time to assess how I had done things
+on a couple of projects I had worked on as an independent contractor. But
+those were about technical decisions I had made and processes I had
+followed. This one would be different. This one was about a personal
+shortcoming, an action of mine that had caused some harm. This one
+involved feelings and emotions.

+

So I asked Amos to facilitate my one-person retro. We’d both done a lot
+of retrospectives, but neither one of us had done anything like this before.
+We had a bit of trouble getting started. We didn’t have any details
+about why the anonymous person felt the way he felt. So it was hard
+to address something that nebulous. So I asked Amos if he could follow
+up with the person. Luckily, the person was online and able to respond
+in real time.

+

So we talked things through some. We went on some tangents. We made some
+progress — things that I could write down as take-aways and action items.
+We got off-topic again, and then came back to it. Then Amos decided to
+play The Why Game. This worked amazingly well. It helped me to find the
+root reasons why I was holding back change. Basically I’m afraid of
+failure. Going deeper, we looked into why I’m afraid of failure. This
+line of inquiry was particularly effective. Hopefully it’ll be enough for
+me to stop being an impediment to change. Here are my notes / take-aways:

+
    +
  • Be careful playing devil’s advocate in public. +
      +
    • People will think I’m taking that stance.
    • +
    +
  • +
  • Don’t always take the boss’s side in public.
  • +
  • Don’t be the person to allow the team to take the easy path.
  • +
  • Why do I think that managers won’t let us try a 2nd time?
  • +
  • Why do I think we might fail the first time? +
      +
    • I want to put us in a better position to succeed.
    • +
    +
  • +
  • I’m being too conservative.
  • +
  • Be willing to fail. Don’t fail to try.
  • +
  • Don’t say we shouldn’t try because I think other people don’t want to try. +
      +
    • That is just self-perpetuating.
    • +
    +
  • +
  • I’m afraid of failing because I haven’t done it much. +
      +
    • Because I’m good at learning from other people’s failures.
    • +
    • But this is holding me back and making me cautious. +
        +
      • Despite the fact that I try to not be cautious.
      • +
      +
    • +
    • So I need to work to counter-act those fears.
    • +
    +
  • +
+

At first, I thought that I’d never heard of anyone doing anything like
+this. But then I recalled that I’d heard about somewhere that everyone
+does this, with their whole team. That would be hard. I’d have to
+trust everyone in the room.

+

But I would recommend this for anyone that can pull it off. It’s hard,
+but it has a large payoff. Just like a team retrospective, it feels good
+making this kind of breakthrough, and knowing that just spending that
+little amount of time has made me a better person. (Mine took about an hour.)
+I think the hardest part is finding someone (or a group of people) you
+trust to be your facilitator.

+

I’ve decided to call this thing an introspective. A retrospective is
+about looking back. This is about looking inward. I’d be interested to
+find out who is doing this kind of thing. Does it have a commonly accepted
+name? How does it work? What techniques work best? If you’ve got any answers or ideas, please comment below, or tweet me @CraigBuchek.

+

So thank you to that anonymous person. Your way of addressing this was
+probably more effective than you could have imagined that it might be.
+I hope that this exercise will help me make the changes required, to make
+me a better person, and help the team become the best that we can be.

+

Amos has written up his thoughts on the introspective.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/agile/retrospectives.html b/public/category/agile/retrospectives.html new file mode 100644 index 0000000..2a65ccf --- /dev/null +++ b/public/category/agile/retrospectives.html @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + +Retrospectives – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Team Values

+ + + +
+

I held a retrospective with my new team last week. The team includes 2 senior developers, 2 junior developers, a product owner, and a product analyst. I’ve joined the team as an engineering manager, which I think of more as a team lead with an elevated title. Being new to this group, I wanted a way to understand their values. What motivates them? What common values do we share that we can leverage to move forward in the same direction?

+

I started out with a pretty simple question: “What do you value (in regards to what we’re building); what are you willing to fight for?” I asked them each to write down several values and then put them on the board before looking at everyone else’s answers. I also asked each person to rank their values in order of importance.

+

It turned out that my question was a little vague. Some people thought about the question in terms of the end result, and some in terms of the process of creating the software. In some ways that was a bit of a problem, because the different interpretations led people in different directions. But in other ways, not pushing them in any particular direction got more varied answers, exposing how they think about the project and the product.

+

My list (in order) was Effectiveness (doing the right thing), Quality (doing things right the first time), Happiness, Purpose, and Teamwork. In retrospect, I should have put Happiness first. If I’m not happy at work, I don’t really want to be there, and need to move on. (Sometimes I can trade some happiness at work for more happiness at home, but I’m definitely a person that needs to be happy at work.) The other values serve to improve my happiness, but the happiness is more important.

+

My own issue ranking my values turned out to be a problem with the ranking in general. Should they be ranked in importance of the necessity of the value as an outcome, or in importance of the necessity to focus on the value? I think the former is what I was looking for, but even I wasn’t clear on that when I began the exercise.

+

After everyone put their values up on the board, we read them off. Then I asked the team to choose several values that we share as a team. We pulled them off the individual members’ lists, and put them in the team list. Then I asked each person to come up and rank those values, then explain why they had ordered them that way, especially when they ordered them significantly different than the last person. I was hoping to come to some convergence of the rank over time, so we could document our values in rank order. That didn’t happen. But the discussion was illuminating to me and to everyone on the team.

+

I think the most interesting part about the lack of convergence was the difference between the developers and the product guys. The product guys definitely viewed the values more in terms of outcomes than the process. That makes sense — they’re not as intimately involved in the process of building the product.

+

We were able to converge on the top priority though: Will the user buy the product? This was a combination of a couple different values that we merged together. This included the end user experience as well as making sure the team would continue to have a reason for existing. The rest of the values we left unordered: Teamwork (cohesiveness), Data driven decisions, Team ownership, Simplicity, Effectiveness, Maintainability / Supportability, Quality, Automation, and Performance. I think that’s a pretty decent list.

+

As a couple teammates pointed out, those values are probably in part a reflection of this current point in time. If I asked the same question some other time, under different conditions and team dynamics, the answers would probably change a bit. And we’d probably come up with other answers if asked again, just due to randomness of the way we think about these things.

+

But I don’t think I’d do this activity a second time with the team. It was really about understanding our motivations — both our own, and those of our teammates. I found it effective in that way, and also in helping the team to think about our culture and how we can work to shape it to help us all push in the same direction.

+

There are a few caveats. When I asked for feedback on the exercise, one teammate pointed out that it wouldn’t work if people weren’t honest about their values, and they answered with what they thought management or their teammates wanted to hear. I don’t think that was an issue with this group, but it’s something to keep in mind.

+

The other major thing I’d do is to make it clear up front that I’m not looking for any action items from this activity; it’s more about understanding each other and ourselves. And next time, I’ll work to clarify how to rank the values.

+

I would recommend this activity for a new team, or when the makeup of the team is changing in some significant way. I wish there was a way to help a team converge on the ranking of their values, but I suppose I should be happy that agreeing on the set of important values went pretty quickly. And the diversity of ideas and opinions is probably a blessing that I should be embracing — the more ideas we have, the wider the variety of solutions we can imagine.

+
+ + +
+ +
+
+ +

2015 Year in Review

+ + + +
+

It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+
+ + +
+ +
+
+ +

Happiness Retrospective

+ + + +
+

I facilitated a retrospective today; it was one of the best retros I’ve ever been involved with. I figured out what activities I wanted to do earlier in the morning. They were really quite simple. I wanted to focus on happiness.

+

How happy are you at work?

+

I started with two questions that I’ve used with teams before, to some success (although not so successful for one particular team). The first question I asked was “How happy are you at work?” I had them put a rating from 0 to 10, with 0 meaning they should have quit last week, and 10 meaning they couldn’t imaging being happier at work.

+

The answers were mostly 7s and 8s, with a 5 and a 9. The average came to 7.5, which is pretty good. The 5 concerns me a bit, especially that it’s 2 points lower than anyone else’s answer.

+

How effective do you think the teams is?

+

The next question I asked was “How effective do you think the teams is?”. Again, from 0 to 10, with 0 meaning they can’t accomplish anything, and 10 meaning you couldn’t imagine a more effective team.

+

When I ask both of these questions, the scores are always highly correlated. If your team isn’t doing good work, it’ll make you unhappy. And if you are unhappy, you’re less likely to do your best work. This team was no different; the 5 and 9 became a 6 and a 10, and most of the 7s became 8s, for an average of just under 8.

+

What makes you happy at work?

+

The next question I asked was “What makes you happy at work?”. The answers were mostly about the teamwork and teammates. This went quicker than I expected. At this point, I was worried the retro would only last a little more than 30 minutes.

+

What would make you happier at work?

+

The final question I asked was “What would make you happier at work?”. This was the real pay-off. We spent about half an hour just talking about the things that would make us happier, and what we could do to improve our happiness. We came up with 10 potential action items. I usually limit teams to trying 3 or 4 action items, but most of the items are quite small, so we’re going to try 6 of them. One is just observing another team’s standup meetings, to see how they’re using their time effectively.

+

Everyone went away feeling that this was a really good retro. It felt good to focus on happiness. Happiness is something I’ve been talking a lot about on the This Agile Life podcast, and it felt good to take some action on it. I’ve done “positive-only” retros before, but this one felt even better than that, by specifically targeting happiness and how we can achieve it.

+
+ + +
+ +
+
+ +

Impromptu Retrospective

+ + + +
+

I’m surprised that I haven’t gotten this story down in print before. It’s something I’ve mentioned many times — including a few times on the podcast. It’s a great story about the power of retrospectives, and it’s a great story about the power of a blameless post-mortem.

+

I don’t recall all the specifics at this point. It was about 5 years ago. I’d just noticed that Arun had made some sort of mistake. That’s fine, people make mistakes. The thing that was different about his mistake was that I had made the same mistake about a week prior. And Amos had made the same mistake about a week before that.

+

Noticing a pattern of mistakes, Amos and I called an impromptu retrospective. We gathered all the developers into a conference room. We explained the problem that we were running into. At first, Arun was defensive. That’s understandable; he thought we were there to come down on him, to lay blame. But we made it clear that we weren’t focusing on him. We admitted that we had also made the same mistake recently. We weren’t there to lay blame; we were there to figure out how our team could stop making the mistake. It took Arun a few minutes to get over the defensiveness.

+

With the defensiveness out of the way, we could focus on the issue at hand. We were able to figure out the root cause of us all making the mistake. (I don’t know if we played the “5 whys” game, but I’m sure we effectively did something similar.) And with that, we were able to change our process, so that nobody else would make the same mistake again.

+

There are 2 important points to this story. First, you don’t have to wait until a scheduled retrospective to hold a retrospective. This one was impromptu, and it’s the best one we ever had. We saw a problem, addressed it, and found a solution in less than an hour. Had we waited until the end of the week, we would have forgotten some of the details, and wouldn’t have been as effective at solving the problem. Second, when addressing problems, take your ego out of the equation. If you’re in a position of authority, take the blame — but never place blame. Focus on what’s important — solving the problem.

+

And don’t forget the Retrospective Prime Directive:

+

Regardless of what we discover, we understand and truly believe that everyone did the best job they could, given what they knew at the time, their skills and abilities, the resources available, and the situation at hand.

+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/agile/retrospectives/feed b/public/category/agile/retrospectives/feed new file mode 100644 index 0000000..9aabe46 --- /dev/null +++ b/public/category/agile/retrospectives/feed @@ -0,0 +1,148 @@ + + + + Retrospectives – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Team Values + http://blog.boochtek.com/2016/01/04/team-values + http://blog.boochtek.com/2016/01/04/team-values#respond + Tue, 05 Jan 2016 04:56:46 +0000 + + + + + http://blog.boochtek.com/?p=294 + Continue reading "Team Values"]]> + I held a retrospective with my new team last week. The team includes 2 senior developers, 2 junior developers, a product owner, and a product analyst. I’ve joined the team as an engineering manager, which I think of more as a team lead with an elevated title. Being new to this group, I wanted a way to understand their values. What motivates them? What common values do we share that we can leverage to move forward in the same direction?

+

I started out with a pretty simple question: “What do you value (in regards to what we’re building); what are you willing to fight for?” I asked them each to write down several values and then put them on the board before looking at everyone else’s answers. I also asked each person to rank their values in order of importance.

+

It turned out that my question was a little vague. Some people thought about the question in terms of the end result, and some in terms of the process of creating the software. In some ways that was a bit of a problem, because the different interpretations led people in different directions. But in other ways, not pushing them in any particular direction got more varied answers, exposing how they think about the project and the product.

+

My list (in order) was Effectiveness (doing the right thing), Quality (doing things right the first time), Happiness, Purpose, and Teamwork. In retrospect, I should have put Happiness first. If I’m not happy at work, I don’t really want to be there, and need to move on. (Sometimes I can trade some happiness at work for more happiness at home, but I’m definitely a person that needs to be happy at work.) The other values serve to improve my happiness, but the happiness is more important.

+

My own issue ranking my values turned out to be a problem with the ranking in general. Should they be ranked in importance of the necessity of the value as an outcome, or in importance of the necessity to focus on the value? I think the former is what I was looking for, but even I wasn’t clear on that when I began the exercise.

+

After everyone put their values up on the board, we read them off. Then I asked the team to choose several values that we share as a team. We pulled them off the individual members’ lists, and put them in the team list. Then I asked each person to come up and rank those values, then explain why they had ordered them that way, especially when they ordered them significantly different than the last person. I was hoping to come to some convergence of the rank over time, so we could document our values in rank order. That didn’t happen. But the discussion was illuminating to me and to everyone on the team.

+

I think the most interesting part about the lack of convergence was the difference between the developers and the product guys. The product guys definitely viewed the values more in terms of outcomes than the process. That makes sense — they’re not as intimately involved in the process of building the product.

+

We were able to converge on the top priority though: Will the user buy the product? This was a combination of a couple different values that we merged together. This included the end user experience as well as making sure the team would continue to have a reason for existing. The rest of the values we left unordered: Teamwork (cohesiveness), Data driven decisions, Team ownership, Simplicity, Effectiveness, Maintainability / Supportability, Quality, Automation, and Performance. I think that’s a pretty decent list.

+

As a couple teammates pointed out, those values are probably in part a reflection of this current point in time. If I asked the same question some other time, under different conditions and team dynamics, the answers would probably change a bit. And we’d probably come up with other answers if asked again, just due to randomness of the way we think about these things.

+

But I don’t think I’d do this activity a second time with the team. It was really about understanding our motivations — both our own, and those of our teammates. I found it effective in that way, and also in helping the team to think about our culture and how we can work to shape it to help us all push in the same direction.

+

There are a few caveats. When I asked for feedback on the exercise, one teammate pointed out that it wouldn’t work if people weren’t honest about their values, and they answered with what they thought management or their teammates wanted to hear. I don’t think that was an issue with this group, but it’s something to keep in mind.

+

The other major thing I’d do is to make it clear up front that I’m not looking for any action items from this activity; it’s more about understanding each other and ourselves. And next time, I’ll work to clarify how to rank the values.

+

I would recommend this activity for a new team, or when the makeup of the team is changing in some significant way. I wish there was a way to help a team converge on the ranking of their values, but I suppose I should be happy that agreeing on the set of important values went pretty quickly. And the diversity of ideas and opinions is probably a blessing that I should be embracing — the more ideas we have, the wider the variety of solutions we can imagine.

+]]>
+ http://blog.boochtek.com/2016/01/04/team-values/feed + 0 +
+ + 2015 Year in Review + http://blog.boochtek.com/2015/12/28/year-in-review-2015 + http://blog.boochtek.com/2015/12/28/year-in-review-2015#respond + Tue, 29 Dec 2015 05:28:38 +0000 + + + + + + + + http://blog.boochtek.com/?p=287 + Continue reading "2015 Year in Review"]]> + It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+]]>
+ http://blog.boochtek.com/2015/12/28/year-in-review-2015/feed + 0 +
+ + Happiness Retrospective + http://blog.boochtek.com/2015/11/09/happiness-retrospective + http://blog.boochtek.com/2015/11/09/happiness-retrospective#respond + Tue, 10 Nov 2015 05:56:58 +0000 + + + + + http://blog.boochtek.com/?p=268 + Continue reading "Happiness Retrospective"]]> + I facilitated a retrospective today; it was one of the best retros I’ve ever been involved with. I figured out what activities I wanted to do earlier in the morning. They were really quite simple. I wanted to focus on happiness.

+

How happy are you at work?

+

I started with two questions that I’ve used with teams before, to some success (although not so successful for one particular team). The first question I asked was “How happy are you at work?” I had them put a rating from 0 to 10, with 0 meaning they should have quit last week, and 10 meaning they couldn’t imaging being happier at work.

+

The answers were mostly 7s and 8s, with a 5 and a 9. The average came to 7.5, which is pretty good. The 5 concerns me a bit, especially that it’s 2 points lower than anyone else’s answer.

+

How effective do you think the teams is?

+

The next question I asked was “How effective do you think the teams is?”. Again, from 0 to 10, with 0 meaning they can’t accomplish anything, and 10 meaning you couldn’t imagine a more effective team.

+

When I ask both of these questions, the scores are always highly correlated. If your team isn’t doing good work, it’ll make you unhappy. And if you are unhappy, you’re less likely to do your best work. This team was no different; the 5 and 9 became a 6 and a 10, and most of the 7s became 8s, for an average of just under 8.

+

What makes you happy at work?

+

The next question I asked was “What makes you happy at work?”. The answers were mostly about the teamwork and teammates. This went quicker than I expected. At this point, I was worried the retro would only last a little more than 30 minutes.

+

What would make you happier at work?

+

The final question I asked was “What would make you happier at work?”. This was the real pay-off. We spent about half an hour just talking about the things that would make us happier, and what we could do to improve our happiness. We came up with 10 potential action items. I usually limit teams to trying 3 or 4 action items, but most of the items are quite small, so we’re going to try 6 of them. One is just observing another team’s standup meetings, to see how they’re using their time effectively.

+

Everyone went away feeling that this was a really good retro. It felt good to focus on happiness. Happiness is something I’ve been talking a lot about on the This Agile Life podcast, and it felt good to take some action on it. I’ve done “positive-only” retros before, but this one felt even better than that, by specifically targeting happiness and how we can achieve it.

+]]>
+ http://blog.boochtek.com/2015/11/09/happiness-retrospective/feed + 0 +
+ + Impromptu Retrospective + http://blog.boochtek.com/2015/11/02/impromptu-retrospective + http://blog.boochtek.com/2015/11/02/impromptu-retrospective#respond + Tue, 03 Nov 2015 03:44:44 +0000 + + + + + http://blog.boochtek.com/?p=265 + Continue reading "Impromptu Retrospective"]]> + I’m surprised that I haven’t gotten this story down in print before. It’s something I’ve mentioned many times — including a few times on the podcast. It’s a great story about the power of retrospectives, and it’s a great story about the power of a blameless post-mortem.

+

I don’t recall all the specifics at this point. It was about 5 years ago. I’d just noticed that Arun had made some sort of mistake. That’s fine, people make mistakes. The thing that was different about his mistake was that I had made the same mistake about a week prior. And Amos had made the same mistake about a week before that.

+

Noticing a pattern of mistakes, Amos and I called an impromptu retrospective. We gathered all the developers into a conference room. We explained the problem that we were running into. At first, Arun was defensive. That’s understandable; he thought we were there to come down on him, to lay blame. But we made it clear that we weren’t focusing on him. We admitted that we had also made the same mistake recently. We weren’t there to lay blame; we were there to figure out how our team could stop making the mistake. It took Arun a few minutes to get over the defensiveness.

+

With the defensiveness out of the way, we could focus on the issue at hand. We were able to figure out the root cause of us all making the mistake. (I don’t know if we played the “5 whys” game, but I’m sure we effectively did something similar.) And with that, we were able to change our process, so that nobody else would make the same mistake again.

+

There are 2 important points to this story. First, you don’t have to wait until a scheduled retrospective to hold a retrospective. This one was impromptu, and it’s the best one we ever had. We saw a problem, addressed it, and found a solution in less than an hour. Had we waited until the end of the week, we would have forgotten some of the details, and wouldn’t have been as effective at solving the problem. Second, when addressing problems, take your ego out of the equation. If you’re in a position of authority, take the blame — but never place blame. Focus on what’s important — solving the problem.

+

And don’t forget the Retrospective Prime Directive:

+

Regardless of what we discover, we understand and truly believe that everyone did the best job they could, given what they knew at the time, their skills and abilities, the resources available, and the situation at hand.

+

 

+]]>
+ http://blog.boochtek.com/2015/11/02/impromptu-retrospective/feed + 0 +
+
+
diff --git a/public/category/agile/tdd.html b/public/category/agile/tdd.html new file mode 100644 index 0000000..a4ea776 --- /dev/null +++ b/public/category/agile/tdd.html @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + +TDD – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

When Should We Do TDD?

+ + + +
+

On a recent episode (78) of This Agile Life, my fellow hosts talked about when to do Test-Driven Development (TDD). They all said that you should always do TDD — at least for anything that will go into production; there’s an exception for experimenting or “spiking”.

+

I wasn’t on that episode, but later commented on the topic. (Episode 83 — which wasn’t really all that great.) My take was slightly different. I said that you should do TDD only when the benefits outweigh the costs. Unfortunately, we usually greatly underestimate the benefits. And the costs often seem high at first, because it takes some time to get good at writing automated tests. Not to mention that both the costs and the benefits are usually hard to measure.

+

What is the cost of writing automated tests? I’ve asked this question before and recorded the answers (inasmuch as we have them) in a previous blog entry. TDD costs us about 10-30% in short-term productivity. The benefit that they found was a reduction in bugs by 30-90%, and a decrease in code complexity by about 30%.

+

But what about when the costs are higher than the normal 10 to 30 percent? One good example of this is when there’s no test framework for the situation you’re testing. This might be a new language or framework that you’re working with. More likely it’s a complex API that you have to mock out. So that could increase the cost of automated testing so as to outweigh the benefits — especially on a short project. I could imagine situations where the cost of writing the mocks could eat up more than the project itself.

+

Another case where we might consider skipping testing is when we’re more concerned about time to market than quality. This is almost always a mistake. Your code will almost always last longer than you expect. (Remember Y2K?) And if the code lasts longer than you expect, that means you’ll have to deal with bugs that whole time. But we have to work with the information we have at the time we make our decisions. And sometimes that might tell us that time to market is more important than anything.

+

One final case I can imagine is when a true expert is coding something that they’re very familiar with. I could picture someone like Uncle Bob writing code (in a language that he’s familiar with) without tests just as effectively as I could write code with tests.

+

But these situations should not be common; they’re edge cases. In almost all real-world cases, TDD is the right thing to do. Don’t forget, TDD is also a design discipline — it helps you design a better API. So keep doing TDD. But as with any practice, don’t do it blindly without considering why we’re doing it. Make sure you understand the costs, and whether the benefits outweigh the costs.

+
+ + +
+ +
+
+ +

TDD Is Alive And Well

+ + + +
+

I went to RailsConf this year, and the very first talk was a keynote by David Heinemeier Hansson (DHH), the creator of Ruby on Rails. The TL;DR of his talk was “TDD rarely has value”. He followed up with a blog post the next day, titled “TDD is dead. Long live testing.“, and 2 more posts. I think this line of thought is terribly misguided, and causing more harm than good. This article is my response.

+

First, I would like to address the good points of the talk. He said that programming is pseudoscience, and that people want to tell us that there’s a secret to being a better programmer. But what it really takes is working hard — reading a lot of code, writing a lot of code, and rewriting a lot of code. He’s right. And I also agree with him that you should forget about patterns for a while when learning to code. Beginners try to throw patterns at a problem instead of letting the patterns emerge where they’re supposed to.

+

I don’t completely agree that programming is a pseudoscience. In some ways it is, but I think it’s more of a craft. It’s a craft, because there’s a lot of science involved, but there’s also an art to doing it well. And like any craft, you’re always working to get better. So to respond to DHH’s stance that “software is more like poetry than physics”, I think it falls
+somewhere in between.

+

With regard to the software engineering practices we use, there really isn’t much science available, mostly because it’s a soft science. That is, it’s really hard to isolate a single variable when comparing code between projects. And nobody has the time or money to write the same code so many times that the differences would be statistically significant.

+

So we don’t have much science on TDD. But we do have some. Here’s a collection of several: StudiesOfTestDrivenDevelopment. And here’s one that explicitly looks are the difference between test-first and test-last: Does Test-Driven Development Really Improve Software Design Quality? What do these tell us? They tell us that TDD costs us about 10-30% in short-term productivity; reduces bugs by 30-90%, and decreases code complexity by about 30%. As Code Complete tells us (in section 20.5, with studies to back it up), improving quality reduces development costs. So, like most Agile practices, this is a case where spending a bit more time in the short term leads to time savings in the long term.

+

The more important lesson in the talk was that you have to do what works best for you and your situation. If TDD doesn’t give better results, then either find out how to make it give better results, or stop using it. As we often say in the Agile world, Agile doesn’t mean that you can stop using your brain. While I think TDD is appropriate in most situations, there are cases where it’s not worth the additional up-front cost. If the most important thing for your project is time-to-market, then not testing might be the right decision for you.

+

To me, TDD provides a bunch of benefits. First and foremost, TDD is a design discipline. It ensures that I think about how my code will be used before I think about how to implement it. This is very powerful in ensuring that the code is well-written from the perspective of other code using it.

+

Tested code provides confidence to be able to make changes without breaking things. If we write tests after the code, we’re less likely to write them. Tests written after the code also tend to test the implementation instead of the desired functionality. What we really want is tests written as a specification. With tests as a specification, we can come back later and understand why code was written. Without tests, or with poor tests, we can’t understand why the code is there; if we want to rewrite it, we don’t have the confidence that we’re not missing something. Writing tests first also ensures that we only write the code that is needed to implement the required functionality.

+

I’m not sure why DHH hasn’t “gotten” TDD. I’m not sure if it’s because he’s  a better coder than average, or if he just thinks in a different way than most of us. I think it’s partly because he doesn’t understand TDD, which he admitted might be the case. And I think he’s conflating TDD and unit testing.

+

DHH is influential in the developer community, especially those newer to Ruby and Rails. People listen to what he has to say. I was happy to see almost every other speaker made fun of DHH’s ideas, and most of the crowd knew better. But there will be a lot of others who will hear DHH, respect his opinions, and not give TDD the try that it deserves. And that’s sad, because it will lead to an overall reduction in code quality in the world.

+

Here are some other people’s thoughts on the matter:

+ +

 

+
+ + +
+ +
+
+ +

Testing Rails Validators

+ + + +
+

It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/agile/tdd/feed b/public/category/agile/tdd/feed new file mode 100644 index 0000000..f92d268 --- /dev/null +++ b/public/category/agile/tdd/feed @@ -0,0 +1,128 @@ + + + + TDD – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + When Should We Do TDD? + http://blog.boochtek.com/2015/05/11/when-tdd + http://blog.boochtek.com/2015/05/11/when-tdd#comments + Tue, 12 May 2015 04:43:05 +0000 + + + + + http://blog.boochtek.com/?p=225 + Continue reading "When Should We Do TDD?"]]> + On a recent episode (78) of This Agile Life, my fellow hosts talked about when to do Test-Driven Development (TDD). They all said that you should always do TDD — at least for anything that will go into production; there’s an exception for experimenting or “spiking”.

+

I wasn’t on that episode, but later commented on the topic. (Episode 83 — which wasn’t really all that great.) My take was slightly different. I said that you should do TDD only when the benefits outweigh the costs. Unfortunately, we usually greatly underestimate the benefits. And the costs often seem high at first, because it takes some time to get good at writing automated tests. Not to mention that both the costs and the benefits are usually hard to measure.

+

What is the cost of writing automated tests? I’ve asked this question before and recorded the answers (inasmuch as we have them) in a previous blog entry. TDD costs us about 10-30% in short-term productivity. The benefit that they found was a reduction in bugs by 30-90%, and a decrease in code complexity by about 30%.

+

But what about when the costs are higher than the normal 10 to 30 percent? One good example of this is when there’s no test framework for the situation you’re testing. This might be a new language or framework that you’re working with. More likely it’s a complex API that you have to mock out. So that could increase the cost of automated testing so as to outweigh the benefits — especially on a short project. I could imagine situations where the cost of writing the mocks could eat up more than the project itself.

+

Another case where we might consider skipping testing is when we’re more concerned about time to market than quality. This is almost always a mistake. Your code will almost always last longer than you expect. (Remember Y2K?) And if the code lasts longer than you expect, that means you’ll have to deal with bugs that whole time. But we have to work with the information we have at the time we make our decisions. And sometimes that might tell us that time to market is more important than anything.

+

One final case I can imagine is when a true expert is coding something that they’re very familiar with. I could picture someone like Uncle Bob writing code (in a language that he’s familiar with) without tests just as effectively as I could write code with tests.

+

But these situations should not be common; they’re edge cases. In almost all real-world cases, TDD is the right thing to do. Don’t forget, TDD is also a design discipline — it helps you design a better API. So keep doing TDD. But as with any practice, don’t do it blindly without considering why we’re doing it. Make sure you understand the costs, and whether the benefits outweigh the costs.

+]]>
+ http://blog.boochtek.com/2015/05/11/when-tdd/feed + 2 +
+ + TDD Is Alive And Well + http://blog.boochtek.com/2014/05/05/tdd-is-alive-and-well + http://blog.boochtek.com/2014/05/05/tdd-is-alive-and-well#comments + Tue, 06 May 2014 04:50:07 +0000 + + + + + + http://blog.boochtek.com/?p=191 + Continue reading "TDD Is Alive And Well"]]> + I went to RailsConf this year, and the very first talk was a keynote by David Heinemeier Hansson (DHH), the creator of Ruby on Rails. The TL;DR of his talk was “TDD rarely has value”. He followed up with a blog post the next day, titled “TDD is dead. Long live testing.“, and 2 more posts. I think this line of thought is terribly misguided, and causing more harm than good. This article is my response.

+

First, I would like to address the good points of the talk. He said that programming is pseudoscience, and that people want to tell us that there’s a secret to being a better programmer. But what it really takes is working hard — reading a lot of code, writing a lot of code, and rewriting a lot of code. He’s right. And I also agree with him that you should forget about patterns for a while when learning to code. Beginners try to throw patterns at a problem instead of letting the patterns emerge where they’re supposed to.

+

I don’t completely agree that programming is a pseudoscience. In some ways it is, but I think it’s more of a craft. It’s a craft, because there’s a lot of science involved, but there’s also an art to doing it well. And like any craft, you’re always working to get better. So to respond to DHH’s stance that “software is more like poetry than physics”, I think it falls
+somewhere in between.

+

With regard to the software engineering practices we use, there really isn’t much science available, mostly because it’s a soft science. That is, it’s really hard to isolate a single variable when comparing code between projects. And nobody has the time or money to write the same code so many times that the differences would be statistically significant.

+

So we don’t have much science on TDD. But we do have some. Here’s a collection of several: StudiesOfTestDrivenDevelopment. And here’s one that explicitly looks are the difference between test-first and test-last: Does Test-Driven Development Really Improve Software Design Quality? What do these tell us? They tell us that TDD costs us about 10-30% in short-term productivity; reduces bugs by 30-90%, and decreases code complexity by about 30%. As Code Complete tells us (in section 20.5, with studies to back it up), improving quality reduces development costs. So, like most Agile practices, this is a case where spending a bit more time in the short term leads to time savings in the long term.

+

The more important lesson in the talk was that you have to do what works best for you and your situation. If TDD doesn’t give better results, then either find out how to make it give better results, or stop using it. As we often say in the Agile world, Agile doesn’t mean that you can stop using your brain. While I think TDD is appropriate in most situations, there are cases where it’s not worth the additional up-front cost. If the most important thing for your project is time-to-market, then not testing might be the right decision for you.

+

To me, TDD provides a bunch of benefits. First and foremost, TDD is a design discipline. It ensures that I think about how my code will be used before I think about how to implement it. This is very powerful in ensuring that the code is well-written from the perspective of other code using it.

+

Tested code provides confidence to be able to make changes without breaking things. If we write tests after the code, we’re less likely to write them. Tests written after the code also tend to test the implementation instead of the desired functionality. What we really want is tests written as a specification. With tests as a specification, we can come back later and understand why code was written. Without tests, or with poor tests, we can’t understand why the code is there; if we want to rewrite it, we don’t have the confidence that we’re not missing something. Writing tests first also ensures that we only write the code that is needed to implement the required functionality.

+

I’m not sure why DHH hasn’t “gotten” TDD. I’m not sure if it’s because he’s  a better coder than average, or if he just thinks in a different way than most of us. I think it’s partly because he doesn’t understand TDD, which he admitted might be the case. And I think he’s conflating TDD and unit testing.

+

DHH is influential in the developer community, especially those newer to Ruby and Rails. People listen to what he has to say. I was happy to see almost every other speaker made fun of DHH’s ideas, and most of the crowd knew better. But there will be a lot of others who will hear DHH, respect his opinions, and not give TDD the try that it deserves. And that’s sad, because it will lead to an overall reduction in code quality in the world.

+

Here are some other people’s thoughts on the matter:

+ +

 

+]]>
+ http://blog.boochtek.com/2014/05/05/tdd-is-alive-and-well/feed + 2 +
+ + Testing Rails Validators + http://blog.boochtek.com/2014/01/26/testing-rails-validators + http://blog.boochtek.com/2014/01/26/testing-rails-validators#respond + Mon, 27 Jan 2014 05:27:07 +0000 + + + + + + http://blog.boochtek.com/?p=93 + Continue reading "Testing Rails Validators"]]> + It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+]]>
+ http://blog.boochtek.com/2014/01/26/testing-rails-validators/feed + 0 +
+
+
diff --git a/public/category/blogging.html b/public/category/blogging.html new file mode 100644 index 0000000..2d3464d --- /dev/null +++ b/public/category/blogging.html @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + +Blogging – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

2015 Year in Review

+ + + +
+

It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+
+ + +
+ +
+
+ +

Resolutions

+ + + +
+

January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+
+ + +
+ +
+
+ +

What I Want in a Blog Engine

+ + + +
+

I’m considering moving away from WordPress, for a couple reasons:

+
    +
  • Security. There have been several vulnerabilities over the past few years, and I’ve never really had a high level of confidence that it’s secure. In addition, I now find the whole PHP model — every web app running as a single user, instead of leveraging UNIX permissions — to be broken.
  • +
  • Speed. I started to realize a couple years ago that a blog engine should generate static pages. The static pages should be updated whenever new content is added. There’s really no reason to re-generate the page every time someone wants to read it. At the very least, Server-Side Includes (SSI) or Edge-Side Includes (ESI) should be used.
  • +
+

Now that I’m starting to actually blog consistently, it makes sense to change. But before I move to something different, I want to be sure that I find all the features that I need. This post is my thought process about what I want.

+

Requirements

+
    +
  • Self hosted. I like to run my own servers. Partly so I can keep full control, and partly because I enjoy doing some system admin stuff (as long as it doesn’t take too much time).
  • +
  • Generation of static pages. Almost everything should be a static page by the time a user requests a page. Obviously, submitting comments would hit a dynamic page, and comment approval would likely be done through a dynamic part of the site. But publishing a blog post or comments should generate static files to be served by Apache or nginx.
  • +
  • Categories (or maybe tags). I want readers to be able to find other things that I’ve written that might interest them. A list of categories in a sidebar would help them find what interests them.
  • +
  • Site map. Especially an XML sitemap, so Google can index better. I also like the idea of readers being able to see everything in one place.
  • +
  • Archives. This is another way of letting readers see everything I’ve written, organized by publication date.
  • +
  • RSS and/or Atom. I want readers to be able to subscribe to my blog.
  • +
  • Media. I don’t think I necessarily need a media manager, just a simple way to add pictures to blog posts.
  • +
  • Comments. I want to allow comments, but I don’t want to use anything like Disqus. I want the comments to be in my own database, so they won’t disappear. I also dislike requiring JavaScript just to see comments. And I’ve worked at places where Disqus wouldn’t work, due to web proxy servers. Comments should be moderated by the site admin, editor, or author. Readers should not have to log in to post a comment, but it would be nice to allow users to log into Twitter or some other social network to provide their info.
  • +
  • Pingbacks and Tweetbacks. I’d like to be able to see who’s referring to my articles.
  • +
+

Nice to Have

+
    +
  • Free. I almost consider this to be a requirement, but I’d be willing to entertain a 1-time payment for something that meets my needs really well. I need to try to counteract my DIY-at-all-non-monetary-costs mentality.
  • +
  • Open Source. I think Open Source is likely to provide a more useful product, especially in this space. An Open Source option is more likely to improve over time.
  • +
  • Not PHP. I’ve got several problems with PHP. First, the whole PHP model of running in the same context as Apache (or nginx) breaks UNIX conventions. A security issue in a PHP program exposes more than just the program itself — it exposes Apache and every other PHP program. Under the UNIX model, each program runs in a different user context, isolating bugs to that program only. PHP is also a “hacky” language. PHP programmers tend to not be as rigorous (in many ways), leading to more vulnerabilities. Finally, the PHP language itself has had several misfeatures that have lead to security issues — much more than Ruby or Java.
  • +
  • Not MySQL. I’d like to be able to remove MySQL from my servers. PostgreSQL would be much preferred. Storing everything in git would be fine too — maybe even preferred. I’d even be fine with MongoDB or some other simple NoSQL database.
  • +
  • Admin console. I can’t imagine approving comments without this, but I’m not closed to that possibility.
  • +
  • Edit history. I want to be able to update posts, but see what the previous versions looked like. (Not sure if I care whether the public can or cannot see the various revisions.)
  • +
  • WYSIWYG. I’d be willing to settle for Markdown. In fact, I’d prefer (extended) Markdown as the canonical form behind the WYSIWYG.
  • +
  • Code highlighting. A lot of my blog posts (will) include code in various languages. I’d like the blogging software to be able to handle that well. Syntax highlighting would be great. Any other related features would also be nice.
  • +
  • Through-the-web editing. I’d like to be able to edit a page from any device from anywhere that has Internet access.
  • +
  • Reader-submitted edits. This is probably going to be the hardest thing to find, but I’d like a really easy way for readers to let me know of typos and such by simply submitting a fix for them. The author or editor of the post would have to approve the edits, obviously.
  • +
  • Import from WordPress. I don’t have a ton of content in WordPress, or else this would probably be a more firm requirement.
  • +
  • Community. Community helps software thrive, adding improvements and ensuring security.
  • +
  • Plugins. It would be nice for the blog engine to be extensible.
  • +
  • Unpublished drafts and previews. I’d really like to be able to see by blog posts before I release them to the world.
  • +
  • CMS abilities. I’d really like the ability to edit non-blog web pages the same way as blog entries.
  • +
  • Themes. It would be nice to have decently looking themes, so I could have other people help me make the site look nice.
  • +
  • Posting my tweets in a sidebar area. I’d be OK with this being done in client-side JavaScript. I’d also be OK with periodic polling of my Twitter feed, and updating the static content when appropriate.
  • +
+

Candidates

+

I’ve been playing with Jekyll lately, using it to generate a simple static site that can be updated easily. It’s pretty nice, and pretty easy to use. Something based on Jekyll would work well. The trickiest part will be figuring out how to manage comments. Or maybe I’ll just have to learn to live more minimally.

+

Octopress is the obvious candidate. It fits most of my needs and wants. Most likely I’ll write my own commenting engine and add it to my Octopress template.

+

The only other candidate that I’ve found is Ghost. It’s written in JavaScript and runs on Node. I don’t find Node to be that compelling, but the templates look great, and it’s designed for coding blogs.

+

I’ll probably take a quick look at Droplets, too. It’s written in PHP, but seems to fit my other requirements.

+

Unfortunately, that seems to be all there is in this space. Maybe I need to investigate more. Or maybe Octopress just has the static blog generation marked cornered. Either way, I’ll probably change my blogging software in the next month or two. Wish me luck!

+

 

+
+ + +
+ +
+
+ +

Introduction

+ + + +
+

Welcome to the BoochTek blog. BoochTek, LLC is a small web development company, based in St. Louis, Missouri. We specialize in Ruby on Rails, jQuery (JavaScript), and PHP.

+

This bog will be a place where we can share some of our experiences in web development, Open Source, programming, and technology in general.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/blogging/feed b/public/category/blogging/feed new file mode 100644 index 0000000..5c2d593 --- /dev/null +++ b/public/category/blogging/feed @@ -0,0 +1,180 @@ + + + + Blogging – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + 2015 Year in Review + http://blog.boochtek.com/2015/12/28/year-in-review-2015 + http://blog.boochtek.com/2015/12/28/year-in-review-2015#respond + Tue, 29 Dec 2015 05:28:38 +0000 + + + + + + + + http://blog.boochtek.com/?p=287 + Continue reading "2015 Year in Review"]]> + It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+]]>
+ http://blog.boochtek.com/2015/12/28/year-in-review-2015/feed + 0 +
+ + Resolutions + http://blog.boochtek.com/2015/02/02/resolutions + http://blog.boochtek.com/2015/02/02/resolutions#respond + Tue, 03 Feb 2015 05:42:13 +0000 + + + + + + + + http://blog.boochtek.com/?p=198 + Continue reading "Resolutions"]]> + January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+]]>
+ http://blog.boochtek.com/2015/02/02/resolutions/feed + 0 +
+ + What I Want in a Blog Engine + http://blog.boochtek.com/2014/02/02/blogging-software + http://blog.boochtek.com/2014/02/02/blogging-software#respond + Mon, 03 Feb 2014 04:50:43 +0000 + + + + + + http://blog.boochtek.com/?p=100 + Continue reading "What I Want in a Blog Engine"]]> + I’m considering moving away from WordPress, for a couple reasons:

+
    +
  • Security. There have been several vulnerabilities over the past few years, and I’ve never really had a high level of confidence that it’s secure. In addition, I now find the whole PHP model — every web app running as a single user, instead of leveraging UNIX permissions — to be broken.
  • +
  • Speed. I started to realize a couple years ago that a blog engine should generate static pages. The static pages should be updated whenever new content is added. There’s really no reason to re-generate the page every time someone wants to read it. At the very least, Server-Side Includes (SSI) or Edge-Side Includes (ESI) should be used.
  • +
+

Now that I’m starting to actually blog consistently, it makes sense to change. But before I move to something different, I want to be sure that I find all the features that I need. This post is my thought process about what I want.

+

Requirements

+
    +
  • Self hosted. I like to run my own servers. Partly so I can keep full control, and partly because I enjoy doing some system admin stuff (as long as it doesn’t take too much time).
  • +
  • Generation of static pages. Almost everything should be a static page by the time a user requests a page. Obviously, submitting comments would hit a dynamic page, and comment approval would likely be done through a dynamic part of the site. But publishing a blog post or comments should generate static files to be served by Apache or nginx.
  • +
  • Categories (or maybe tags). I want readers to be able to find other things that I’ve written that might interest them. A list of categories in a sidebar would help them find what interests them.
  • +
  • Site map. Especially an XML sitemap, so Google can index better. I also like the idea of readers being able to see everything in one place.
  • +
  • Archives. This is another way of letting readers see everything I’ve written, organized by publication date.
  • +
  • RSS and/or Atom. I want readers to be able to subscribe to my blog.
  • +
  • Media. I don’t think I necessarily need a media manager, just a simple way to add pictures to blog posts.
  • +
  • Comments. I want to allow comments, but I don’t want to use anything like Disqus. I want the comments to be in my own database, so they won’t disappear. I also dislike requiring JavaScript just to see comments. And I’ve worked at places where Disqus wouldn’t work, due to web proxy servers. Comments should be moderated by the site admin, editor, or author. Readers should not have to log in to post a comment, but it would be nice to allow users to log into Twitter or some other social network to provide their info.
  • +
  • Pingbacks and Tweetbacks. I’d like to be able to see who’s referring to my articles.
  • +
+

Nice to Have

+
    +
  • Free. I almost consider this to be a requirement, but I’d be willing to entertain a 1-time payment for something that meets my needs really well. I need to try to counteract my DIY-at-all-non-monetary-costs mentality.
  • +
  • Open Source. I think Open Source is likely to provide a more useful product, especially in this space. An Open Source option is more likely to improve over time.
  • +
  • Not PHP. I’ve got several problems with PHP. First, the whole PHP model of running in the same context as Apache (or nginx) breaks UNIX conventions. A security issue in a PHP program exposes more than just the program itself — it exposes Apache and every other PHP program. Under the UNIX model, each program runs in a different user context, isolating bugs to that program only. PHP is also a “hacky” language. PHP programmers tend to not be as rigorous (in many ways), leading to more vulnerabilities. Finally, the PHP language itself has had several misfeatures that have lead to security issues — much more than Ruby or Java.
  • +
  • Not MySQL. I’d like to be able to remove MySQL from my servers. PostgreSQL would be much preferred. Storing everything in git would be fine too — maybe even preferred. I’d even be fine with MongoDB or some other simple NoSQL database.
  • +
  • Admin console. I can’t imagine approving comments without this, but I’m not closed to that possibility.
  • +
  • Edit history. I want to be able to update posts, but see what the previous versions looked like. (Not sure if I care whether the public can or cannot see the various revisions.)
  • +
  • WYSIWYG. I’d be willing to settle for Markdown. In fact, I’d prefer (extended) Markdown as the canonical form behind the WYSIWYG.
  • +
  • Code highlighting. A lot of my blog posts (will) include code in various languages. I’d like the blogging software to be able to handle that well. Syntax highlighting would be great. Any other related features would also be nice.
  • +
  • Through-the-web editing. I’d like to be able to edit a page from any device from anywhere that has Internet access.
  • +
  • Reader-submitted edits. This is probably going to be the hardest thing to find, but I’d like a really easy way for readers to let me know of typos and such by simply submitting a fix for them. The author or editor of the post would have to approve the edits, obviously.
  • +
  • Import from WordPress. I don’t have a ton of content in WordPress, or else this would probably be a more firm requirement.
  • +
  • Community. Community helps software thrive, adding improvements and ensuring security.
  • +
  • Plugins. It would be nice for the blog engine to be extensible.
  • +
  • Unpublished drafts and previews. I’d really like to be able to see by blog posts before I release them to the world.
  • +
  • CMS abilities. I’d really like the ability to edit non-blog web pages the same way as blog entries.
  • +
  • Themes. It would be nice to have decently looking themes, so I could have other people help me make the site look nice.
  • +
  • Posting my tweets in a sidebar area. I’d be OK with this being done in client-side JavaScript. I’d also be OK with periodic polling of my Twitter feed, and updating the static content when appropriate.
  • +
+

Candidates

+

I’ve been playing with Jekyll lately, using it to generate a simple static site that can be updated easily. It’s pretty nice, and pretty easy to use. Something based on Jekyll would work well. The trickiest part will be figuring out how to manage comments. Or maybe I’ll just have to learn to live more minimally.

+

Octopress is the obvious candidate. It fits most of my needs and wants. Most likely I’ll write my own commenting engine and add it to my Octopress template.

+

The only other candidate that I’ve found is Ghost. It’s written in JavaScript and runs on Node. I don’t find Node to be that compelling, but the templates look great, and it’s designed for coding blogs.

+

I’ll probably take a quick look at Droplets, too. It’s written in PHP, but seems to fit my other requirements.

+

Unfortunately, that seems to be all there is in this space. Maybe I need to investigate more. Or maybe Octopress just has the static blog generation marked cornered. Either way, I’ll probably change my blogging software in the next month or two. Wish me luck!

+

 

+]]>
+ http://blog.boochtek.com/2014/02/02/blogging-software/feed + 0 +
+ + Introduction + http://blog.boochtek.com/2010/01/01/introduction + Fri, 01 Jan 2010 06:01:41 +0000 + + + + http://blog.boochtek.com/?p=8 + + Welcome to the BoochTek blog. BoochTek, LLC is a small web development company, based in St. Louis, Missouri. We specialize in Ruby on Rails, jQuery (JavaScript), and PHP.

+

This bog will be a place where we can share some of our experiences in web development, Open Source, programming, and technology in general.

+]]>
+
+
+
diff --git a/public/category/gadgets.html b/public/category/gadgets.html new file mode 100644 index 0000000..c539426 --- /dev/null +++ b/public/category/gadgets.html @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + +Gadgets – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Chording Keyers

+ + + +
+

I’m considering buying a chording keyer. A keyer is a 1-handed text input device. Chording means that you can hit more than 1 key at a time. I’ve been interested in these for a long time actually. They’ve been popular in the wearable computing (“cyborg”) field, but never caught on anywhere else. I’ve always thought that 1-handed chording keyer would be a great input device to use with a cell phone. It’d be even better with a heads-up display like the Google Glass.

+

There are quite a few chording keyers available. It doesn’t appear that any will meet exactly what I’m really looking for, but I might take the leap anyway. It should be a hand-held device. I really want a Bluetooth device, so I can use it with a computer, phone, or tablet. Below are the ones I’m considering.

+

Twiddler

+

The Twiddler has been around quite a while, as probably the main commercially available keyer for wearable computing. There was a time that it was unavailable, but there’s a new version available from HandyKey now. It’s $200 ($220 Canadian), and comes in only USB.

+

The Twiddler looks like a small TV remote. The current version (2.1) has 4 thumb keys, a thumb joystick (for mouse), and 12 small finger keys. It has good community support, including an alternate chord layout.

+

In10did Decatxt

+

In10did has been trying to bring out a few variations of their devices. The Decatxt looks like a pack of cigarettes, with 8 keys on the front and 2 thumb keys. It can be used with 1 or 2 hands. All letters can be typed with 1 finger or 1 thumb and 1 finger.

+

The Decatxt has USB and Bluetooth models, but I’m unable to find pricing or a way to buy one. It looks like there have been no updates for over a year, so I’m concerned that this is going to be vaporware.

+

CyKey

+

The CyKey is based on the Microwriter, which goes back to the early 1980s. The Microwriter was a desktop-based device, but it looks like the CyKey might be able to be used as a hand-held device. It has 9 buttons on the face, arranged in 3 groups of 3 buttons.

+

The CyKey is $125 (£75) and works over InfraRed connected to USB. A Bluetooth model is supposedly in the works.

+

EkaPad

+

The EkaPad is like a cross between the Decatxt and the Twiddler. It has 12 buttons on the face, but no thumb keys. It’s got a nice well-thought-out design that looks quite ergonomic.

+

The EkaPad is $150, available in USB only. A desktop holder is available to hold it upright, or it can be used hand-held.

+

FrogPad

+

The FrogPad is meant to be used on a flat surface only. It looks kind of like a third of a laptop keyboard. It’s got 15 regular keys and 5 modifier keys. Only 1 key and 1 modifier can be chorded. Right-handed and left-handed models are available, with USB connectivity.

+

The FrogPad2 was recently announced, replacing the original FrogPad. It switches to standard desktop-style keys, and adds some more keys along the top. It supports USB On-the-Go and Bluetooth. I’m not really interested in this model with its regular keys, but it doesn’t appear that the original version is being made any longer. The new models are priced at $200. I believe the old models were $150.

+

Chordite

+

The Chordite looks the most promising, but it’s only a concept with some DIY prototypes. It’s a hand-held device, with 2 buttons on each of the 4 fingers — one activated by the distal phalanx and the other by the middle phalanx. This initially sounds like it would be difficult to press 2 different buttons with 1 finger, but I think the way they’re positioned would make it actually work really well.

+

Septambic Keyer

+

The septambic keyer is another DIY concept, hand-built and used by wearable computing folks. It’s fitted to your palm, and you squeeze your fingers toward a fist to activate the buttons. There’s one button for each of the 4 fingers, and 3 for the thumb.

+

Clove 2

+

The Clove 2 is a Bluetooth-enabled glove with 5 buttons — 1 for each finger. It’s another do-it-yourself project.

+

Software Options

+

I found an interesting project called ASETNIOP that allows chording on a standard Qwerty keyboard. (Assuming the keyboard supports multi-key roll-over well enough. Most keyboards should work.) It’s got a really nice online interactive tutorial. You basically chord with the 8 home-row keys, plus the spacebar.

+

For Android, a program called GKOS for Android uses multi-touch with 1 or 2 button presses. The buttons are different sizes, depending on their frequency. I like this concept, but I don’t think the implementation is quite ready for production.

+

I also found an app for iOS called Nintype that is a combination between Swype and a chording keyer.

+

Conclusion

+

I think I’m going to try the Twiddler. If I find the concept to be a good one, hopefully I’ll build myself a Chordite (or have someone build one for me).

+

I’m also going to play with implementing some of the ideas from ASETNIOP, using KeyRemap4MacBook.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/gadgets/feed b/public/category/gadgets/feed new file mode 100644 index 0000000..147fc0b --- /dev/null +++ b/public/category/gadgets/feed @@ -0,0 +1,65 @@ + + + + Gadgets – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Chording Keyers + http://blog.boochtek.com/2014/02/23/chording-keyers + http://blog.boochtek.com/2014/02/23/chording-keyers#comments + Mon, 24 Feb 2014 05:56:37 +0000 + + + + http://blog.boochtek.com/?p=124 + Continue reading "Chording Keyers"]]> + I’m considering buying a chording keyer. A keyer is a 1-handed text input device. Chording means that you can hit more than 1 key at a time. I’ve been interested in these for a long time actually. They’ve been popular in the wearable computing (“cyborg”) field, but never caught on anywhere else. I’ve always thought that 1-handed chording keyer would be a great input device to use with a cell phone. It’d be even better with a heads-up display like the Google Glass.

+

There are quite a few chording keyers available. It doesn’t appear that any will meet exactly what I’m really looking for, but I might take the leap anyway. It should be a hand-held device. I really want a Bluetooth device, so I can use it with a computer, phone, or tablet. Below are the ones I’m considering.

+

Twiddler

+

The Twiddler has been around quite a while, as probably the main commercially available keyer for wearable computing. There was a time that it was unavailable, but there’s a new version available from HandyKey now. It’s $200 ($220 Canadian), and comes in only USB.

+

The Twiddler looks like a small TV remote. The current version (2.1) has 4 thumb keys, a thumb joystick (for mouse), and 12 small finger keys. It has good community support, including an alternate chord layout.

+

In10did Decatxt

+

In10did has been trying to bring out a few variations of their devices. The Decatxt looks like a pack of cigarettes, with 8 keys on the front and 2 thumb keys. It can be used with 1 or 2 hands. All letters can be typed with 1 finger or 1 thumb and 1 finger.

+

The Decatxt has USB and Bluetooth models, but I’m unable to find pricing or a way to buy one. It looks like there have been no updates for over a year, so I’m concerned that this is going to be vaporware.

+

CyKey

+

The CyKey is based on the Microwriter, which goes back to the early 1980s. The Microwriter was a desktop-based device, but it looks like the CyKey might be able to be used as a hand-held device. It has 9 buttons on the face, arranged in 3 groups of 3 buttons.

+

The CyKey is $125 (£75) and works over InfraRed connected to USB. A Bluetooth model is supposedly in the works.

+

EkaPad

+

The EkaPad is like a cross between the Decatxt and the Twiddler. It has 12 buttons on the face, but no thumb keys. It’s got a nice well-thought-out design that looks quite ergonomic.

+

The EkaPad is $150, available in USB only. A desktop holder is available to hold it upright, or it can be used hand-held.

+

FrogPad

+

The FrogPad is meant to be used on a flat surface only. It looks kind of like a third of a laptop keyboard. It’s got 15 regular keys and 5 modifier keys. Only 1 key and 1 modifier can be chorded. Right-handed and left-handed models are available, with USB connectivity.

+

The FrogPad2 was recently announced, replacing the original FrogPad. It switches to standard desktop-style keys, and adds some more keys along the top. It supports USB On-the-Go and Bluetooth. I’m not really interested in this model with its regular keys, but it doesn’t appear that the original version is being made any longer. The new models are priced at $200. I believe the old models were $150.

+

Chordite

+

The Chordite looks the most promising, but it’s only a concept with some DIY prototypes. It’s a hand-held device, with 2 buttons on each of the 4 fingers — one activated by the distal phalanx and the other by the middle phalanx. This initially sounds like it would be difficult to press 2 different buttons with 1 finger, but I think the way they’re positioned would make it actually work really well.

+

Septambic Keyer

+

The septambic keyer is another DIY concept, hand-built and used by wearable computing folks. It’s fitted to your palm, and you squeeze your fingers toward a fist to activate the buttons. There’s one button for each of the 4 fingers, and 3 for the thumb.

+

Clove 2

+

The Clove 2 is a Bluetooth-enabled glove with 5 buttons — 1 for each finger. It’s another do-it-yourself project.

+

Software Options

+

I found an interesting project called ASETNIOP that allows chording on a standard Qwerty keyboard. (Assuming the keyboard supports multi-key roll-over well enough. Most keyboards should work.) It’s got a really nice online interactive tutorial. You basically chord with the 8 home-row keys, plus the spacebar.

+

For Android, a program called GKOS for Android uses multi-touch with 1 or 2 button presses. The buttons are different sizes, depending on their frequency. I like this concept, but I don’t think the implementation is quite ready for production.

+

I also found an app for iOS called Nintype that is a combination between Swype and a chording keyer.

+

Conclusion

+

I think I’m going to try the Twiddler. If I find the concept to be a good one, hopefully I’ll build myself a Chordite (or have someone build one for me).

+

I’m also going to play with implementing some of the ideas from ASETNIOP, using KeyRemap4MacBook.

+]]>
+ http://blog.boochtek.com/2014/02/23/chording-keyers/feed + 3 +
+
+
diff --git a/public/category/open-source.html b/public/category/open-source.html new file mode 100644 index 0000000..84ff3f1 --- /dev/null +++ b/public/category/open-source.html @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + +Open Source – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Burying the Lede

+ + + +
+

Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+
+ + +
+ +
+
+ +

What’s Your Open Source Resolution?

+ + + +
+

I’ve been using GNU/Linux since 1994, starting with a Slackware 2.2 CD-ROM I ordered from the back of a magazine. I’ve been heavily involved in my local GNU/Linux community since 1999, and I give frequent presentations at various groups. I’ve made some small contributions to several Open Source projects.

+

But I don’t feel like I’ve done enough to contribute to the wider community. So this year, I’m going to resolve to step up my contributions, and do more.

+

Change my role in the LUG

+

I was lucky enough this past year to find someone interested in helping to run the St. Louis GNU/Linux Users Group (STLLUG). I’ve been running the group so long that I’ve somewhat stagnated. It’s great to find someone who will bring a fresh energy and new ideas. Thanks, Andrew!

+

Mostly freed from the more general administrative tasks (mostly planning meeting topics and speakers), I’m hoping to find the time to work on a few projects. First, I want to overhaul the web site. It needs a new look, as well as an easier way to update it. I’m planning to implement some sort of CMS — possibly WordPress or Octopress, or at the very least, a git repo to manage updates.

+

Blog regularly

+

I installed WordPress a long time ago, but I haven’t published many posts. I’ve got a few drafts in various stages. So this year I’m going to actually get the blog going, and hopefully post about once a week. I’ll be sharing things I learn on the Internet, as well as some of the things I learn from my experiences with various technologies and techniques.

+

Contribute more significant patches

+

There are a few items I’ve been meaning to work on and contribute. I’d like to finally get around to them:

+
    +
  • Make the GNU sort command be able to (easily) sort a list of IP addresses, or semantic version numbers.
  • +
  • Add MySQL-style commands to PostgreSQL: SHOW DATABASES; SHOW TABLES; DESCRIBE table. (I’m not sure how welcome these additions might be, but I think they’d make transitioning from MySQL easier for people.)
  • +
+

Publish and maintain several Ruby gems

+

I’ve been working on a few Ruby gems. I’d like to get them to a state where they’re useful to more people. These include:

+
    +
  • A way to simplify standard CRUD controller actions
  • +
  • A way to declare list views in a manner similar to SimpleForm or Formtastic
  • +
  • A way to declare ActiveRecord attributes in models using Virtus
  • +
  • A higher-level interface to ROM (Ruby Object Mapper)
  • +
  • A similar high-level way to define Perpetuity attributes in models using Virtus
  • +
  • My Rails template, making it much easier to start a Rails app
  • +
+

Build some apps

+

I enjoyed building a quick Rails app during last year’s Rails Rumble, even if it doesn’t turn out to be useful or make any money. I’d like to create a few small apps again this year, either in Rails or Apache Cordova. I’ve got some ideas — we’ll see how if they turn into anything useful or worth selling.

+

Podcast more often

+

Last year, I started joining the This Agile Life podcast. I enjoy it. I’m hoping to continue with that, and possibly doing some podcasting about Ruby and Rails.

+

Present at a conference

+

I’m going to attempt to give a presentation at a conference. I often give presentations at local groups, but haven’t addressed a larger audience. I’ve got a few ideas rattling around, and will see if I can turn them into presentations worthy of a larger conference.

+

What’s your Open Source resolution?

+
+ + +
+ +
+
+ +

Bulk Rename in Bash

+ + + +
+

Here’s a relatively simple way to rename a bunch of files from the command line. It uses sed within a command substitution to compute the new names from the old names.

+

In this example, we’re renaming files that start with “ABC” to start with “XYZ” instead:

+
  for i in ABC*; do mv $i $(echo $i | sed -e s/^ABC/XYZ/); done
+

You’ll have to use shell globbing (wildcards) in the first part, to determine which files will be the source of the renaming, and regular expressions in the second part to translate the old names into the new names.

+

It’s a good idea to use echo in place of mv before running this, to see what the results will be, so you don’t make a mistake.

+

Depending on the situation, you may need to adapt this for your particular needs. For example, if you have too many files to rename, you would need to use xargs. Or sed might not be up for the task of transforming the names that you want. Or you might need to use find to find files within a hierarchy. As with any UNIX idiom, there are hundreds of variations on the theme.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/open-source/feed b/public/category/open-source/feed new file mode 100644 index 0000000..57634bf --- /dev/null +++ b/public/category/open-source/feed @@ -0,0 +1,126 @@ + + + + Open Source – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Burying the Lede + http://blog.boochtek.com/2014/03/11/readable-shell-scripts + http://blog.boochtek.com/2014/03/11/readable-shell-scripts#respond + Wed, 12 Mar 2014 04:28:22 +0000 + + + + + + http://blog.boochtek.com/?p=110 + Continue reading "Burying the Lede"]]> + Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+]]>
+ http://blog.boochtek.com/2014/03/11/readable-shell-scripts/feed + 0 +
+ + What’s Your Open Source Resolution? + http://blog.boochtek.com/2014/01/04/open-source-resolutions + http://blog.boochtek.com/2014/01/04/open-source-resolutions#respond + Sun, 05 Jan 2014 05:02:42 +0000 + + + + http://blog.boochtek.com/?p=14 + Continue reading "What’s Your Open Source Resolution?"]]> + I’ve been using GNU/Linux since 1994, starting with a Slackware 2.2 CD-ROM I ordered from the back of a magazine. I’ve been heavily involved in my local GNU/Linux community since 1999, and I give frequent presentations at various groups. I’ve made some small contributions to several Open Source projects.

+

But I don’t feel like I’ve done enough to contribute to the wider community. So this year, I’m going to resolve to step up my contributions, and do more.

+

Change my role in the LUG

+

I was lucky enough this past year to find someone interested in helping to run the St. Louis GNU/Linux Users Group (STLLUG). I’ve been running the group so long that I’ve somewhat stagnated. It’s great to find someone who will bring a fresh energy and new ideas. Thanks, Andrew!

+

Mostly freed from the more general administrative tasks (mostly planning meeting topics and speakers), I’m hoping to find the time to work on a few projects. First, I want to overhaul the web site. It needs a new look, as well as an easier way to update it. I’m planning to implement some sort of CMS — possibly WordPress or Octopress, or at the very least, a git repo to manage updates.

+

Blog regularly

+

I installed WordPress a long time ago, but I haven’t published many posts. I’ve got a few drafts in various stages. So this year I’m going to actually get the blog going, and hopefully post about once a week. I’ll be sharing things I learn on the Internet, as well as some of the things I learn from my experiences with various technologies and techniques.

+

Contribute more significant patches

+

There are a few items I’ve been meaning to work on and contribute. I’d like to finally get around to them:

+
    +
  • Make the GNU sort command be able to (easily) sort a list of IP addresses, or semantic version numbers.
  • +
  • Add MySQL-style commands to PostgreSQL: SHOW DATABASES; SHOW TABLES; DESCRIBE table. (I’m not sure how welcome these additions might be, but I think they’d make transitioning from MySQL easier for people.)
  • +
+

Publish and maintain several Ruby gems

+

I’ve been working on a few Ruby gems. I’d like to get them to a state where they’re useful to more people. These include:

+
    +
  • A way to simplify standard CRUD controller actions
  • +
  • A way to declare list views in a manner similar to SimpleForm or Formtastic
  • +
  • A way to declare ActiveRecord attributes in models using Virtus
  • +
  • A higher-level interface to ROM (Ruby Object Mapper)
  • +
  • A similar high-level way to define Perpetuity attributes in models using Virtus
  • +
  • My Rails template, making it much easier to start a Rails app
  • +
+

Build some apps

+

I enjoyed building a quick Rails app during last year’s Rails Rumble, even if it doesn’t turn out to be useful or make any money. I’d like to create a few small apps again this year, either in Rails or Apache Cordova. I’ve got some ideas — we’ll see how if they turn into anything useful or worth selling.

+

Podcast more often

+

Last year, I started joining the This Agile Life podcast. I enjoy it. I’m hoping to continue with that, and possibly doing some podcasting about Ruby and Rails.

+

Present at a conference

+

I’m going to attempt to give a presentation at a conference. I often give presentations at local groups, but haven’t addressed a larger audience. I’ve got a few ideas rattling around, and will see if I can turn them into presentations worthy of a larger conference.

+

What’s your Open Source resolution?

+]]>
+ http://blog.boochtek.com/2014/01/04/open-source-resolutions/feed + 0 +
+ + Bulk Rename in Bash + http://blog.boochtek.com/2011/08/26/bulk-rename-in-bash + http://blog.boochtek.com/2011/08/26/bulk-rename-in-bash#respond + Sat, 27 Aug 2011 03:53:58 +0000 + + + + + http://blog.boochtek.com/?p=46 + Continue reading "Bulk Rename in Bash"]]> + Here’s a relatively simple way to rename a bunch of files from the command line. It uses sed within a command substitution to compute the new names from the old names.

+

In this example, we’re renaming files that start with “ABC” to start with “XYZ” instead:

+
  for i in ABC*; do mv $i $(echo $i | sed -e s/^ABC/XYZ/); done
+

You’ll have to use shell globbing (wildcards) in the first part, to determine which files will be the source of the renaming, and regular expressions in the second part to translate the old names into the new names.

+

It’s a good idea to use echo in place of mv before running this, to see what the results will be, so you don’t make a mistake.

+

Depending on the situation, you may need to adapt this for your particular needs. For example, if you have too many files to rename, you would need to use xargs. Or sed might not be up for the task of transforming the names that you want. Or you might need to use find to find files within a hierarchy. As with any UNIX idiom, there are hundreds of variations on the theme.

+]]>
+ http://blog.boochtek.com/2011/08/26/bulk-rename-in-bash/feed + 0 +
+
+
diff --git a/public/category/open-source/linux.html b/public/category/open-source/linux.html new file mode 100644 index 0000000..1a080f9 --- /dev/null +++ b/public/category/open-source/linux.html @@ -0,0 +1,248 @@ + + + + + + + + + + + + + + + +GNU/Linux – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Burying the Lede

+ + + +
+

Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+
+ + +
+ +
+
+ +

Bulk Rename in Bash

+ + + +
+

Here’s a relatively simple way to rename a bunch of files from the command line. It uses sed within a command substitution to compute the new names from the old names.

+

In this example, we’re renaming files that start with “ABC” to start with “XYZ” instead:

+
  for i in ABC*; do mv $i $(echo $i | sed -e s/^ABC/XYZ/); done
+

You’ll have to use shell globbing (wildcards) in the first part, to determine which files will be the source of the renaming, and regular expressions in the second part to translate the old names into the new names.

+

It’s a good idea to use echo in place of mv before running this, to see what the results will be, so you don’t make a mistake.

+

Depending on the situation, you may need to adapt this for your particular needs. For example, if you have too many files to rename, you would need to use xargs. Or sed might not be up for the task of transforming the names that you want. Or you might need to use find to find files within a hierarchy. As with any UNIX idiom, there are hundreds of variations on the theme.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/open-source/linux/feed b/public/category/open-source/linux/feed new file mode 100644 index 0000000..b44a6ca --- /dev/null +++ b/public/category/open-source/linux/feed @@ -0,0 +1,82 @@ + + + + GNU/Linux – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Burying the Lede + http://blog.boochtek.com/2014/03/11/readable-shell-scripts + http://blog.boochtek.com/2014/03/11/readable-shell-scripts#respond + Wed, 12 Mar 2014 04:28:22 +0000 + + + + + + http://blog.boochtek.com/?p=110 + Continue reading "Burying the Lede"]]> + Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+]]>
+ http://blog.boochtek.com/2014/03/11/readable-shell-scripts/feed + 0 +
+ + Bulk Rename in Bash + http://blog.boochtek.com/2011/08/26/bulk-rename-in-bash + http://blog.boochtek.com/2011/08/26/bulk-rename-in-bash#respond + Sat, 27 Aug 2011 03:53:58 +0000 + + + + + http://blog.boochtek.com/?p=46 + Continue reading "Bulk Rename in Bash"]]> + Here’s a relatively simple way to rename a bunch of files from the command line. It uses sed within a command substitution to compute the new names from the old names.

+

In this example, we’re renaming files that start with “ABC” to start with “XYZ” instead:

+
  for i in ABC*; do mv $i $(echo $i | sed -e s/^ABC/XYZ/); done
+

You’ll have to use shell globbing (wildcards) in the first part, to determine which files will be the source of the renaming, and regular expressions in the second part to translate the old names into the new names.

+

It’s a good idea to use echo in place of mv before running this, to see what the results will be, so you don’t make a mistake.

+

Depending on the situation, you may need to adapt this for your particular needs. For example, if you have too many files to rename, you would need to use xargs. Or sed might not be up for the task of transforming the names that you want. Or you might need to use find to find files within a hierarchy. As with any UNIX idiom, there are hundreds of variations on the theme.

+]]>
+ http://blog.boochtek.com/2011/08/26/bulk-rename-in-bash/feed + 0 +
+
+
diff --git a/public/category/programming.html b/public/category/programming.html new file mode 100644 index 0000000..a755a6c --- /dev/null +++ b/public/category/programming.html @@ -0,0 +1,588 @@ + + + + + + + + + + + + + + + +Programming – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

2015 Year in Review

+ + + +
+

It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+
+ + +
+ +
+
+ +

The Ultimate Optimization

+ + + +
+

I got my first computer in 1984. It was a Commodore 64. I had to do extra chores and save up my allowance to buy it. I was 13.

+

Back then, Sears and other retail stores had Commodore 64 computers out on display. Whenever I went to the store, I’d write a short BASIC program and leave it running on the display computers:

+
10 PRINT "CRAIG IS GREAT!"
+20 GOTO 10
+

Hey, I was a 13-year-old kid. Later, I got a little more sophisticated. I’d change the background color instead. I still remember the POKE address:

+
10 FOR I=0 TO 255
+20 POKE 53281, I
+30 NEXT I
+40 GOTO 10
+RUN
+

This was fast enough to change the background color every few scan lines, creating a flashing scrolling effect.

+

Later, I learned 6502 assembly language. I translated the BASIC program into assembler, and memorized the bytes to type in at the store. In assembly language, the background color would change several times per scan line. The effect was kind of psychedelic.

+

All that happened in the mid-1980s.

+

Fast-forward to about 2000 or so. I was telling the above story after a St. Louis LUG meeting. I explained how I had memorized the 10 or 12 bytes of machine code, and would leave the program running with its psychedelic effect.

+

After thinking about it for a bit, I thought that 10 or 12 bytes seemed too much. It actually bothered me — I couldn’t fall asleep when I got home. I got up and found my old 6502 manuals. I figured out how to write the code in 7 bytes. I installed the Vice C64 emulator on my Linux desktop, and tested my code. It worked as expected. (The emulator was already clock-cycle perfect by then.) Here’s the assembly code:

+
INX         ; $E8           ; 232
+STX $D021   ; $8E $21 $D0   ; 142 33 208   ; $D021 = 53281
+JMP $C000   ; $4C $00 $C0   ; 76 0 192     ; $C000 = 49152
+

Here’s the BASIC program to store that program in memory and run it:

+
10 FOR N=49152 TO 49152+6: READ Q : POKE N, Q : NEXT
+20 DATA 232, 142, 33, 208, 76, 0, 192
+30 SYS 49152
+RUN
+

The moral of the story is that you can optimize even a 10-byte program, 15 years after the last time it was used. So don’t tell me that your program can’t be improved, no matter how small it is.

+

PS. I rewrote the code above while writing this article in 2015, about 15 years after the last time I rewrote it. And I again downloaded Vice to test it, this time on Mac OS X.

+
+ + +
+ +
+
+ +

Not Quite Callbacks

+ + + +
+

I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+
+ + +
+ +
+
+ +

Architectural Thoughts

+ + + +
+

I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+
+ + +
+ +
+
+ +

Hexagonal Rails Controllers

+ + + +
+

I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+
+ + +
+ +
+
+ +

Resolutions

+ + + +
+

January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+
+ + +
+ +
+
+ +

Ruby Pattern: Parameterized Module Inclusion

+ + + +
+

I’ve run across a pattern in Ruby lately that I really like. It solves some problems that I’ve struggled with for several years. Let me start with the problems.

+

Let’s say you want to include an ORM in a model class, and want to tell it what database table to use. Typically, you’d do this:

+
class User
+  include MyORM::Model
+  table 'people'
+end
+

But that table call is more like an option to the module inclusion than anything else. So what we’d really like is something like this:

+
class User
+  include MyORM::Model, table: 'people'
+end
+

But that’s not valid Ruby; include doesn’t let you pass anything other than a module.

+

So when I was learning about Virtus, I noticed that its example of how to include it is a bit different than the standard Ruby idiomatic include:

+
class User
+  include Virtus.model
+end
+

At first glance, it reads like the first example. But on closer inspection and consideration, it’s quite a bit different. Where MyORM::Model is a constant that refers to a module, Virtus.model is a method call. So there’s a method named model in the Virtus module. That method returns another module — which is exactly what’s needed in order to include it into our model class.

+

The easiest way to implement Virtus.model would be this:

+
module Virtus
+  def model
+    ::Virtus::Model
+  end
+end
+
+module Virtus::Model
+  # ...
+end
+

If Virtus.model doesn’t need to take any arguments, that’s perfectly fine. In fact, I’ve started to use this implementation of the pattern for modules that don’t need parameters.

+

Because Virtus.model is a method, we can also call it with options:

+
class User
+  include Virtus.model(constructor: false, mass_assignment: false)
+end
+

We could even pass a block. But how do we process those options? There are a few different ways. However we do it, we have to be sure to return a module. And we can create modules in a few different ways.

+

Virtus uses the builder pattern. It takes the parameters passed in and builds a module dynamically. By that, I mean that it calls Module.new and then adds methods to that module. It does this by mixing in other modules, but it could do it by dynamically defining methods as well.

+

I’ve never seen this pattern in any other language. It’s obviously only possible because we can dynamically create modules.

+

The use of this idiom seems to be catching on a bit in the Ruby community. I’ve started using it myself, and will be adding it to my Includable::ActiveRecord gem soon.

+

 

+

 

+
+ + +
+ +
+
+ +

Brilliant – My Very Own Programming Language

+ + + +
+

I’ve decided to design and implement my own programming language, and call it Brilliant.

+

I’ve been interested in programming languages and linguistics almost as long as I’ve been using computers. I’ve long thought that if I ever go back to college, it’s likely that I’ll concentrate on programming languages as a specialty.

+

My recent discovery and involvement with the Crystal programming language has gotten me excited about new language ideas. It’s also helped me realize that implementing a language myself is feasible, and that there’s no better time to start than now.

+

Implementation

+

For now, the Brilliant implementation doesn’t let you do much more than “Hello World”. But you gotta start somewhere. So this article isn’t really “Introducing Brilliant” so much as the start of a series of articles on its design and implementation.

+

I wanted to use a PEG (parsing expression grammar) parser for several reasons. For one, they seem to have gained a lot of popularity in the past few years. Also, PEGs cannot be ambiguous, which can solve a few difficult problems, such as the “dangling else“. Perhaps my favorite feature of PEG grammars is that you don’t need a separate tokenizer (lexer). This provides a nice advantage in that we can use keywords like “class” as variables, as long as they’re not used in a place where the keyword would make sense.

+

So knowing that I wanted to use a PEG, I had to find a PEG parser. I kind of wanted to use ANTLR, which has been a leading parser for many years. But the PEG seems to be new to version 4, and I couldn’t find any Ruby bindings for version 4. Treetop seems to be the most popular parser for Ruby, but I found the EBNF format that Rattler uses to be more to my taste. I think the fact that it’s newer also gives it a few advantages, having had a chance to learn some lessons from Treetop.

+

I thought about using the Rubinius VM, but decided to go with LLVM, mainly since it has slightly better docs for Ruby, and because it’s what Crystal uses. Also, it’s pretty easy to get it to compile to a binary executable or run in a JIT. In the future, I might consider switching to the Rubinius VM, the Erlang VM, or the Perl 6 VM (Parrot). But for now, I like the idea of being able to compile to a binary and easily interface with C, just like Crystal.

+

Goals

+

My main goal is to have fun playing around with language ideas.

+

I’ve found a really great language in Ruby, so I’ll be using it as a starting point. But Ruby does have its faults. In some ways, I want to answer the question “what would Ruby look like if we designed it today?”.

+

But I also want to explore other ideas. What if objects defaulted to immutable? What if functions and methods were assumed to be pure by default? Might it be possible to infer the purity of a function or method? (If so, we could automatically memoize them.) Can we make creating an actor as easy as creating an object?

+

I’ll also be looking at ideas from other programming languages. Could we get some of the benefits of Haskell’s purity without having to do somersaults to do IO? Could we mix Python’s indentation style scoping with brace style or begin/end style? Could we integrate Icon’s ideas about success and failure (goal-directed execution)? What interesting features can we pull from Ada, Io, CoffeeScript, Crystal, Rubinius, Perl 6, etc.?
+

+

I’m not so much as interested in cutting-edge features, as in features that can be easily used by the average programmer. More importantly, I’m interested in how features interact with each other, so they fit well together to create a whole that’s greater than the sum of the parts.

+

Naming

+

I wanted to name my programming language “Fermat”. I’ve long been intrigued by Fermat’s Last Theorem, and Fermat’s Little Theorem is important in number theory. Unfortunately, there’s already a computer algebra system with that name.

+

So I decided to find a name in the same vein as “Ruby” and “Crystal”. I looked at the Wikipedia page for “gemstones” for some inspiration. A lot of the names of gemstones are already taken. I considered some obscure gemstones, but saw the word “brilliant” and thought it was decent. It’s not the name of another gemstone, but still evokes some similarity to Ruby and Crystal.

+

So that’s the name for now. Perhaps I’ll decide to change it at some point in the future, but I needed a name for the project, as well as a file extension for source code files. I chose “bril” for that. I suppose “br” would be a better choice. Perhaps I’ll change that before the next article in this series.

+

Future

+

I hope to work on Brilliant every once in a while. I expect it’ll take a couple years before it’s really very useful.

+

When I do add a major feature, I’ll be certain to blog about it. I’ve got tons of ideas strewn about in various files. It would be great to get them organized and published —and even better to get them implemented.

+
+ + +
+ +
+
+ +

Slow Down!

+ + + +
+

There’s a tweet that I saw recently, with some simple advice for novice programmers:

+

Slow down.

+

This is probably good advice for most programmers. Our team recently noticed that every time we try to rush things, we make mistakes. And the mistakes end up costing us more time than if we had just done things at our normal pace. Slowing down ensures that you do things right, and when you do things right, you end up with a higher-quality product.

+

Speed and Code Quality

+

There are 2 types of code quality: internal and external. External code quality can be measured by how many bugs have been reported by customers. Internal code quality is harder to measure, but it mainly deals with the ability to change the code. When your internal quality is low, you’ve got lots of technical debt, and it’s harder to make changes.

+

So when you try to write code quickly, code quality decreases, leading to a code base that takes more time to make changes to. Conversely, when you slow down, your code quality improves, and it becomes easier to make changes more quickly. So when writing code, slowing down in the short run leads to a speed-up in the long run.

+

Speed and Process Improvement

+

But writing code isn’t the only place where we try to speed up. On an Agile team, we’re always trying to improve the way we work — especially at the beginning stages of an Agile transformation. So we’re eager to make changes in our processes. But I’d urge you to slow down here as well.

+

My colleague Amos and I frequently argue over pair switching. It’s funny, because we agree on everything except for 1 small detail. We both think pair switching is very important, to ensure that team members see more of what’s going on, to bring more ideas to each story, to prevent knowledge silos, and to encourage team ownership. Where we disagree is how long an ideal pairing session should last. I think pairs should switch every 2 hours, and he thinks 1 hour is ideal. I’ve seen teams reach the 1 hour pairing sessions successfully. But usually not without some pain and even often failing at the first attempt.

+

There’s nothing inherently wrong with failing. But if you fail at something, you’re not likely to try again. After all, you should learn from your failures, right?

+

So if you want your team to do something, you probably don’t want them to fail at it. If they fail, they won’t want to try a second time. That’s just human nature, and learning from failure. While you might think that they failed because they weren’t ready for the change yet, they’ll most likely think that they failed because this particular change won’t work for their situation. And they probably won’t know what to change when trying again, so they won’t try again.

+

I’ve seen this over and over. Back when Linux was up-and-coming, when a consultant pushed a company into using Linux before they were ready for it, and it didn’t work out, that company was cautious about trying again. So instead of being on the leading edge of using Linux, or even the middle of the pack, they ended up more toward the trailing edge. Had they not been pushed, they would have gotten more benefit in the long run.

+

So my advice in process improvement is the same as in programming: slow down. Take small steps toward what you think is the ideal. Make a small change, see how it works out, and adjust. As long as you’re still moving in the right direction, I believe you’ll move faster by taking small steps than by trying to make big leaps.

+
+ + +
+ +
+
+ +

Burying the Lede

+ + + +
+

Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/programming/architecture.html b/public/category/programming/architecture.html new file mode 100644 index 0000000..00fe3b4 --- /dev/null +++ b/public/category/programming/architecture.html @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + +Architecture – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Not Quite Callbacks

+ + + +
+

I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+
+ + +
+ +
+
+ +

Architectural Thoughts

+ + + +
+

I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/programming/architecture/feed b/public/category/programming/architecture/feed new file mode 100644 index 0000000..b6d8b27 --- /dev/null +++ b/public/category/programming/architecture/feed @@ -0,0 +1,91 @@ + + + + Architecture – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Not Quite Callbacks + http://blog.boochtek.com/2015/06/22/not-quite-callbacks + http://blog.boochtek.com/2015/06/22/not-quite-callbacks#respond + Tue, 23 Jun 2015 04:55:17 +0000 + + + + + + http://blog.boochtek.com/?p=245 + Continue reading "Not Quite Callbacks"]]> + I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+]]>
+ http://blog.boochtek.com/2015/06/22/not-quite-callbacks/feed + 0 +
+ + Architectural Thoughts + http://blog.boochtek.com/2015/06/01/architectural-thoughts + http://blog.boochtek.com/2015/06/01/architectural-thoughts#respond + Tue, 02 Jun 2015 04:51:59 +0000 + + + + + + + http://blog.boochtek.com/?p=240 + Continue reading "Architectural Thoughts"]]> + I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+]]>
+ http://blog.boochtek.com/2015/06/01/architectural-thoughts/feed + 0 +
+
+
diff --git a/public/category/programming/feed b/public/category/programming/feed new file mode 100644 index 0000000..868c1df --- /dev/null +++ b/public/category/programming/feed @@ -0,0 +1,432 @@ + + + + Programming – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + 2015 Year in Review + http://blog.boochtek.com/2015/12/28/year-in-review-2015 + http://blog.boochtek.com/2015/12/28/year-in-review-2015#respond + Tue, 29 Dec 2015 05:28:38 +0000 + + + + + + + + http://blog.boochtek.com/?p=287 + Continue reading "2015 Year in Review"]]> + It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+]]>
+ http://blog.boochtek.com/2015/12/28/year-in-review-2015/feed + 0 +
+ + The Ultimate Optimization + http://blog.boochtek.com/2015/12/06/ultimate-optimization + http://blog.boochtek.com/2015/12/06/ultimate-optimization#respond + Mon, 07 Dec 2015 05:28:43 +0000 + + + + http://blog.boochtek.com/?p=277 + Continue reading "The Ultimate Optimization"]]> + I got my first computer in 1984. It was a Commodore 64. I had to do extra chores and save up my allowance to buy it. I was 13.

+

Back then, Sears and other retail stores had Commodore 64 computers out on display. Whenever I went to the store, I’d write a short BASIC program and leave it running on the display computers:

+
10 PRINT "CRAIG IS GREAT!"
+20 GOTO 10
+

Hey, I was a 13-year-old kid. Later, I got a little more sophisticated. I’d change the background color instead. I still remember the POKE address:

+
10 FOR I=0 TO 255
+20 POKE 53281, I
+30 NEXT I
+40 GOTO 10
+RUN
+

This was fast enough to change the background color every few scan lines, creating a flashing scrolling effect.

+

Later, I learned 6502 assembly language. I translated the BASIC program into assembler, and memorized the bytes to type in at the store. In assembly language, the background color would change several times per scan line. The effect was kind of psychedelic.

+

All that happened in the mid-1980s.

+

Fast-forward to about 2000 or so. I was telling the above story after a St. Louis LUG meeting. I explained how I had memorized the 10 or 12 bytes of machine code, and would leave the program running with its psychedelic effect.

+

After thinking about it for a bit, I thought that 10 or 12 bytes seemed too much. It actually bothered me — I couldn’t fall asleep when I got home. I got up and found my old 6502 manuals. I figured out how to write the code in 7 bytes. I installed the Vice C64 emulator on my Linux desktop, and tested my code. It worked as expected. (The emulator was already clock-cycle perfect by then.) Here’s the assembly code:

+
INX         ; $E8           ; 232
+STX $D021   ; $8E $21 $D0   ; 142 33 208   ; $D021 = 53281
+JMP $C000   ; $4C $00 $C0   ; 76 0 192     ; $C000 = 49152
+

Here’s the BASIC program to store that program in memory and run it:

+
10 FOR N=49152 TO 49152+6: READ Q : POKE N, Q : NEXT
+20 DATA 232, 142, 33, 208, 76, 0, 192
+30 SYS 49152
+RUN
+

The moral of the story is that you can optimize even a 10-byte program, 15 years after the last time it was used. So don’t tell me that your program can’t be improved, no matter how small it is.

+

PS. I rewrote the code above while writing this article in 2015, about 15 years after the last time I rewrote it. And I again downloaded Vice to test it, this time on Mac OS X.

+]]>
+ http://blog.boochtek.com/2015/12/06/ultimate-optimization/feed + 0 +
+ + Not Quite Callbacks + http://blog.boochtek.com/2015/06/22/not-quite-callbacks + http://blog.boochtek.com/2015/06/22/not-quite-callbacks#respond + Tue, 23 Jun 2015 04:55:17 +0000 + + + + + + http://blog.boochtek.com/?p=245 + Continue reading "Not Quite Callbacks"]]> + I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+]]>
+ http://blog.boochtek.com/2015/06/22/not-quite-callbacks/feed + 0 +
+ + Architectural Thoughts + http://blog.boochtek.com/2015/06/01/architectural-thoughts + http://blog.boochtek.com/2015/06/01/architectural-thoughts#respond + Tue, 02 Jun 2015 04:51:59 +0000 + + + + + + + http://blog.boochtek.com/?p=240 + Continue reading "Architectural Thoughts"]]> + I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+]]>
+ http://blog.boochtek.com/2015/06/01/architectural-thoughts/feed + 0 +
+ + Hexagonal Rails Controllers + http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers + http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers#respond + Tue, 24 Feb 2015 05:50:28 +0000 + + + + + + http://blog.boochtek.com/?p=202 + Continue reading "Hexagonal Rails Controllers"]]> + I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+]]>
+ http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers/feed + 0 +
+ + Resolutions + http://blog.boochtek.com/2015/02/02/resolutions + http://blog.boochtek.com/2015/02/02/resolutions#respond + Tue, 03 Feb 2015 05:42:13 +0000 + + + + + + + + http://blog.boochtek.com/?p=198 + Continue reading "Resolutions"]]> + January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+]]>
+ http://blog.boochtek.com/2015/02/02/resolutions/feed + 0 +
+ + Ruby Pattern: Parameterized Module Inclusion + http://blog.boochtek.com/2014/04/14/ruby-parameterized-module-inclusion + http://blog.boochtek.com/2014/04/14/ruby-parameterized-module-inclusion#comments + Tue, 15 Apr 2014 04:57:29 +0000 + + + + http://blog.boochtek.com/?p=184 + Continue reading "Ruby Pattern: Parameterized Module Inclusion"]]> + I’ve run across a pattern in Ruby lately that I really like. It solves some problems that I’ve struggled with for several years. Let me start with the problems.

+

Let’s say you want to include an ORM in a model class, and want to tell it what database table to use. Typically, you’d do this:

+
class User
+  include MyORM::Model
+  table 'people'
+end
+

But that table call is more like an option to the module inclusion than anything else. So what we’d really like is something like this:

+
class User
+  include MyORM::Model, table: 'people'
+end
+

But that’s not valid Ruby; include doesn’t let you pass anything other than a module.

+

So when I was learning about Virtus, I noticed that its example of how to include it is a bit different than the standard Ruby idiomatic include:

+
class User
+  include Virtus.model
+end
+

At first glance, it reads like the first example. But on closer inspection and consideration, it’s quite a bit different. Where MyORM::Model is a constant that refers to a module, Virtus.model is a method call. So there’s a method named model in the Virtus module. That method returns another module — which is exactly what’s needed in order to include it into our model class.

+

The easiest way to implement Virtus.model would be this:

+
module Virtus
+  def model
+    ::Virtus::Model
+  end
+end
+
+module Virtus::Model
+  # ...
+end
+

If Virtus.model doesn’t need to take any arguments, that’s perfectly fine. In fact, I’ve started to use this implementation of the pattern for modules that don’t need parameters.

+

Because Virtus.model is a method, we can also call it with options:

+
class User
+  include Virtus.model(constructor: false, mass_assignment: false)
+end
+

We could even pass a block. But how do we process those options? There are a few different ways. However we do it, we have to be sure to return a module. And we can create modules in a few different ways.

+

Virtus uses the builder pattern. It takes the parameters passed in and builds a module dynamically. By that, I mean that it calls Module.new and then adds methods to that module. It does this by mixing in other modules, but it could do it by dynamically defining methods as well.

+

I’ve never seen this pattern in any other language. It’s obviously only possible because we can dynamically create modules.

+

The use of this idiom seems to be catching on a bit in the Ruby community. I’ve started using it myself, and will be adding it to my Includable::ActiveRecord gem soon.

+

 

+

 

+]]>
+ http://blog.boochtek.com/2014/04/14/ruby-parameterized-module-inclusion/feed + 2 +
+ + Brilliant – My Very Own Programming Language + http://blog.boochtek.com/2014/03/30/brilliant-my-own-programming-language + http://blog.boochtek.com/2014/03/30/brilliant-my-own-programming-language#comments + Mon, 31 Mar 2014 04:25:38 +0000 + + + + + http://blog.boochtek.com/?p=172 + Continue reading "Brilliant – My Very Own Programming Language"]]> + I’ve decided to design and implement my own programming language, and call it Brilliant.

+

I’ve been interested in programming languages and linguistics almost as long as I’ve been using computers. I’ve long thought that if I ever go back to college, it’s likely that I’ll concentrate on programming languages as a specialty.

+

My recent discovery and involvement with the Crystal programming language has gotten me excited about new language ideas. It’s also helped me realize that implementing a language myself is feasible, and that there’s no better time to start than now.

+

Implementation

+

For now, the Brilliant implementation doesn’t let you do much more than “Hello World”. But you gotta start somewhere. So this article isn’t really “Introducing Brilliant” so much as the start of a series of articles on its design and implementation.

+

I wanted to use a PEG (parsing expression grammar) parser for several reasons. For one, they seem to have gained a lot of popularity in the past few years. Also, PEGs cannot be ambiguous, which can solve a few difficult problems, such as the “dangling else“. Perhaps my favorite feature of PEG grammars is that you don’t need a separate tokenizer (lexer). This provides a nice advantage in that we can use keywords like “class” as variables, as long as they’re not used in a place where the keyword would make sense.

+

So knowing that I wanted to use a PEG, I had to find a PEG parser. I kind of wanted to use ANTLR, which has been a leading parser for many years. But the PEG seems to be new to version 4, and I couldn’t find any Ruby bindings for version 4. Treetop seems to be the most popular parser for Ruby, but I found the EBNF format that Rattler uses to be more to my taste. I think the fact that it’s newer also gives it a few advantages, having had a chance to learn some lessons from Treetop.

+

I thought about using the Rubinius VM, but decided to go with LLVM, mainly since it has slightly better docs for Ruby, and because it’s what Crystal uses. Also, it’s pretty easy to get it to compile to a binary executable or run in a JIT. In the future, I might consider switching to the Rubinius VM, the Erlang VM, or the Perl 6 VM (Parrot). But for now, I like the idea of being able to compile to a binary and easily interface with C, just like Crystal.

+

Goals

+

My main goal is to have fun playing around with language ideas.

+

I’ve found a really great language in Ruby, so I’ll be using it as a starting point. But Ruby does have its faults. In some ways, I want to answer the question “what would Ruby look like if we designed it today?”.

+

But I also want to explore other ideas. What if objects defaulted to immutable? What if functions and methods were assumed to be pure by default? Might it be possible to infer the purity of a function or method? (If so, we could automatically memoize them.) Can we make creating an actor as easy as creating an object?

+

I’ll also be looking at ideas from other programming languages. Could we get some of the benefits of Haskell’s purity without having to do somersaults to do IO? Could we mix Python’s indentation style scoping with brace style or begin/end style? Could we integrate Icon’s ideas about success and failure (goal-directed execution)? What interesting features can we pull from Ada, Io, CoffeeScript, Crystal, Rubinius, Perl 6, etc.?
+

+

I’m not so much as interested in cutting-edge features, as in features that can be easily used by the average programmer. More importantly, I’m interested in how features interact with each other, so they fit well together to create a whole that’s greater than the sum of the parts.

+

Naming

+

I wanted to name my programming language “Fermat”. I’ve long been intrigued by Fermat’s Last Theorem, and Fermat’s Little Theorem is important in number theory. Unfortunately, there’s already a computer algebra system with that name.

+

So I decided to find a name in the same vein as “Ruby” and “Crystal”. I looked at the Wikipedia page for “gemstones” for some inspiration. A lot of the names of gemstones are already taken. I considered some obscure gemstones, but saw the word “brilliant” and thought it was decent. It’s not the name of another gemstone, but still evokes some similarity to Ruby and Crystal.

+

So that’s the name for now. Perhaps I’ll decide to change it at some point in the future, but I needed a name for the project, as well as a file extension for source code files. I chose “bril” for that. I suppose “br” would be a better choice. Perhaps I’ll change that before the next article in this series.

+

Future

+

I hope to work on Brilliant every once in a while. I expect it’ll take a couple years before it’s really very useful.

+

When I do add a major feature, I’ll be certain to blog about it. I’ve got tons of ideas strewn about in various files. It would be great to get them organized and published —and even better to get them implemented.

+]]>
+ http://blog.boochtek.com/2014/03/30/brilliant-my-own-programming-language/feed + 1 +
+ + Slow Down! + http://blog.boochtek.com/2014/03/16/slow-down + http://blog.boochtek.com/2014/03/16/slow-down#respond + Mon, 17 Mar 2014 04:14:45 +0000 + + + + + http://blog.boochtek.com/?p=154 + Continue reading "Slow Down!"]]> + There’s a tweet that I saw recently, with some simple advice for novice programmers:

+

Slow down.

+

This is probably good advice for most programmers. Our team recently noticed that every time we try to rush things, we make mistakes. And the mistakes end up costing us more time than if we had just done things at our normal pace. Slowing down ensures that you do things right, and when you do things right, you end up with a higher-quality product.

+

Speed and Code Quality

+

There are 2 types of code quality: internal and external. External code quality can be measured by how many bugs have been reported by customers. Internal code quality is harder to measure, but it mainly deals with the ability to change the code. When your internal quality is low, you’ve got lots of technical debt, and it’s harder to make changes.

+

So when you try to write code quickly, code quality decreases, leading to a code base that takes more time to make changes to. Conversely, when you slow down, your code quality improves, and it becomes easier to make changes more quickly. So when writing code, slowing down in the short run leads to a speed-up in the long run.

+

Speed and Process Improvement

+

But writing code isn’t the only place where we try to speed up. On an Agile team, we’re always trying to improve the way we work — especially at the beginning stages of an Agile transformation. So we’re eager to make changes in our processes. But I’d urge you to slow down here as well.

+

My colleague Amos and I frequently argue over pair switching. It’s funny, because we agree on everything except for 1 small detail. We both think pair switching is very important, to ensure that team members see more of what’s going on, to bring more ideas to each story, to prevent knowledge silos, and to encourage team ownership. Where we disagree is how long an ideal pairing session should last. I think pairs should switch every 2 hours, and he thinks 1 hour is ideal. I’ve seen teams reach the 1 hour pairing sessions successfully. But usually not without some pain and even often failing at the first attempt.

+

There’s nothing inherently wrong with failing. But if you fail at something, you’re not likely to try again. After all, you should learn from your failures, right?

+

So if you want your team to do something, you probably don’t want them to fail at it. If they fail, they won’t want to try a second time. That’s just human nature, and learning from failure. While you might think that they failed because they weren’t ready for the change yet, they’ll most likely think that they failed because this particular change won’t work for their situation. And they probably won’t know what to change when trying again, so they won’t try again.

+

I’ve seen this over and over. Back when Linux was up-and-coming, when a consultant pushed a company into using Linux before they were ready for it, and it didn’t work out, that company was cautious about trying again. So instead of being on the leading edge of using Linux, or even the middle of the pack, they ended up more toward the trailing edge. Had they not been pushed, they would have gotten more benefit in the long run.

+

So my advice in process improvement is the same as in programming: slow down. Take small steps toward what you think is the ideal. Make a small change, see how it works out, and adjust. As long as you’re still moving in the right direction, I believe you’ll move faster by taking small steps than by trying to make big leaps.

+]]>
+ http://blog.boochtek.com/2014/03/16/slow-down/feed + 0 +
+ + Burying the Lede + http://blog.boochtek.com/2014/03/11/readable-shell-scripts + http://blog.boochtek.com/2014/03/11/readable-shell-scripts#respond + Wed, 12 Mar 2014 04:28:22 +0000 + + + + + + http://blog.boochtek.com/?p=110 + Continue reading "Burying the Lede"]]> + Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+]]>
+ http://blog.boochtek.com/2014/03/11/readable-shell-scripts/feed + 0 +
+
+
diff --git a/public/category/programming/oop.html b/public/category/programming/oop.html new file mode 100644 index 0000000..84c37a0 --- /dev/null +++ b/public/category/programming/oop.html @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + +OOP – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Hexagonal Rails Controllers

+ + + +
+

I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+
+ + +
+ +
+
+ +

Includable ActiveRecord

+ + + +
+

I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+
+ + +
+ +
+
+ +

Testing Rails Validators

+ + + +
+

It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/programming/oop/feed b/public/category/programming/oop/feed new file mode 100644 index 0000000..1205e40 --- /dev/null +++ b/public/category/programming/oop/feed @@ -0,0 +1,173 @@ + + + + OOP – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Hexagonal Rails Controllers + http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers + http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers#respond + Tue, 24 Feb 2015 05:50:28 +0000 + + + + + + http://blog.boochtek.com/?p=202 + Continue reading "Hexagonal Rails Controllers"]]> + I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+]]>
+ http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers/feed + 0 +
+ + Includable ActiveRecord + http://blog.boochtek.com/2014/02/10/includable-activerecord + http://blog.boochtek.com/2014/02/10/includable-activerecord#respond + Mon, 10 Feb 2014 18:09:53 +0000 + + + + + + http://blog.boochtek.com/?p=104 + Continue reading "Includable ActiveRecord"]]> + I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+]]>
+ http://blog.boochtek.com/2014/02/10/includable-activerecord/feed + 0 +
+ + Testing Rails Validators + http://blog.boochtek.com/2014/01/26/testing-rails-validators + http://blog.boochtek.com/2014/01/26/testing-rails-validators#respond + Mon, 27 Jan 2014 05:27:07 +0000 + + + + + + http://blog.boochtek.com/?p=93 + Continue reading "Testing Rails Validators"]]> + It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+]]>
+ http://blog.boochtek.com/2014/01/26/testing-rails-validators/feed + 0 +
+
+
diff --git a/public/category/programming/page/2.html b/public/category/programming/page/2.html new file mode 100644 index 0000000..342f560 --- /dev/null +++ b/public/category/programming/page/2.html @@ -0,0 +1,554 @@ + + + + + + + + + + + + + + + +Programming – Page 2 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Yak Shaving #1: Cursor Keys

+ + + +
+

I recently decided to start using Emacs again. I used it extensively from the early 1990s until the early 2000s. I pretty much stopped using it when I had a sysadmin job with no Emacs on the servers, and no ability to install it. With the rising popularity of tmux and tmate for remote pairing, and my dislike for vim’s modes, I decided to try going back to Emacs in the terminal.

+

One thing I really want in a text editor is good cursor key support. Shifted cursor keys
+should select text, and Control+left and right should move by words. (Apple HIG says to use Option+left and right to move by words; most apps on Mac OS X seem to support both.) Things have worked this way on almost every text editor on every OS I’ve used — Amiga, DOS, Windows, NeXT, Mac, Motif, Gnome, KDE. It’s a part of the CUA standard that’s been in common usage on everything since the mid-1980s.

+

Enabling cursor keys in Emacs was pretty easy. I’ve decided to use Prelude to make getting started with Emacs easy. Emacs comes with the cursor keys enabled, but Prelude disables them. Undoing Prelude’s change is pretty easy:

+
(setq prelude-guru nil)
+

Trying to make shifted cursor keys work is where the trouble began. They work in the GUI version of Emacs, but not from the terminal. It turns out that the Mac Terminal doesn’t distinguish between cursor keys and shifted cursor keys in its default configuration. So I had to figure out how to configure key bindings in Terminal.

+

That’s easy enough — they’re in the preferences. But what should I set them to? This took a lot of research. Terminal emulation and ANSI code sequences are obscure, complex, and inconsistent. I eventually found the info I needed. For starters, Shift+Right, Shift+Left, Shift+Home, and Shift+End are defined in the terminfo. The rest I was able to piece together from various sources around the Internet.

+

I’m also trying to script my Mac configuration. So instead of manually adding all the keybindings in the Terminal preferences pane, I decided to write a script. Mac OS X does a decent job of allowing you to change preferences from the command line. For example, to always show the tab bar:

+
defaults write -app Terminal ShowTabBar 1
+

Easy enough, except for a couple problems. First, I had to figure out the obscure syntax used in the preferences for key codes. I was able to piece these together with some more Internet research. But the really big problem is that the keyboard bindings are 2 levels deep within a “dictionary” (hash map). And the defaults command doesn’t handle that. There are some obscure utilities that handle nested preferences, but they don’t work well with the preferences caching in Mac OS X 10.9 — a problem I ran into while testing.

+

So now I’m writing a utility in Python that does what the defaults command does, but that will handle nested dictionaries.

+

There’s a term for this process of having to solve one issue before you can solve another, and the issues get several layers deep. It’s called yak shaving.

+

Here’s another good example:

+
Father from Malcolm in the middle having to fix one thing before fixing another, ad infinitum.
Yak shaving – home repairs
+

I’m sure this will be the first of many posts about me yak shaving.

+
+ + +
+ +
+
+ +

Includable ActiveRecord

+ + + +
+

I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+
+ + +
+ +
+
+ +

Testing Rails Validators

+ + + +
+

It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+
+ + +
+ +
+
+ +

My Thoughts on Python vs. Ruby

+ + + +
+

I’ve been using Python at work for the past few months.  I learned Python back in the early 2000s, but never used it for any large projects.  I learned Ruby in late 2005, and it quickly became my language of choice for most cases.

+

So while I still prefer Ruby, and will likely use Ruby more in the future than Python, I wanted to assess the strengths and weaknesses of Python in relation to Ruby.  Perhaps some of the lessons could be applied when writing Ruby, and it could help to decide when to use each.  Also, I’m interested in programming language design, and wanted to document pros and cons in that light.

+

Where Python Sucks (As Compared to Ruby)

+
    +
  • Have to explicitly include self in EVERY method declaration. +
      +
    • Including @classmethod declarations (although people usually use the name cls instead of self there).
    • +
    • Except @staticmethod declarations.
    • +
    +
  • +
  • Have to use self everywhere to reference an object’s own attributes.
  • +
  • Inconsistency of things like the len() function, when everything else is a method.
  • +
  • Inconsistency of having some built-in classes with lower-case names. +
      +
    • And they’re not whole words, so you have to memorize them: list, dict, str, int.
    • +
    • Even more bizarre is dict vs. OrderedDict.
    • +
    +
  • +
  • I’m not a fan of True and False being uppercase. +
      +
    • I’m a bit less concerned about None, for some reason.
    • +
    • If they’re going to be uppercase, they might as well be all caps, in my opinion.
    • +
    +
  • +
  • Inconsistency of camelCase versus snake_case in some modules.
  • +
  • Claims to prefer explicit over implicit, yet does not use new to create an object. +
      +
    • It is nicely concise, but makes it look too much like a regular function call.
    • +
    +
  • +
  • The implementation of lambdas is too limited. +
      +
    • Makes using map function not very useful.
    • +
    +
  • +
  • Class methods are less than ideal to implement. +
      +
    • Probably better to use functions within a module instead, in many cases.
    • +
    +
  • +
  • Lack of good functional programming tools makes it harder to manipulate lists. +
      +
    • Have to often resort to creating an initial list and adding to it in a for loop.
    • +
    +
  • +
  • I don’t understand why r’regex_string’ doesn’t just create an actual regular expression object.
  • +
  • I miss Ruby’s method-call-or-property-getter syntax. +
      +
    • Nice that I can get it by adding @property to method definitions, but that’s a bit messy.
    • +
    +
  • +
  • I don’t understand why lists don’t have a join() method; it seems backwards to call join on the string used to connect the list elements.
  • +
  • I miss unless; seems like with all the keywords, Python would have added that.
  • +
  • I really miss ||= to memoize. +
      +
    • The distinction between unset variables and variables set to None makes it hard.
    • +
    • I also miss +=.
    • +
    +
  • +
  • I really miss statement modifiers (if or unless at the end of the statement/expression instead of the beginning).
  • +
  • I miss being able to assign to a compound statement (x = if True: 1; else: 2). +
      +
    • While Python does allow this simple case, it does not allow more complex cases.
    • +
    +
  • +
  • The way modules, files, and classes work, I either have a lot of classes in one file, or have to come up with more module names in order to split classes into different files.
  • +
  • The preference for bare functions within modules over class methods often leads to functions outside of classes, when they’d make more sense more closely associated with the class.
  • +
  • I don’t quite understand the necessity for pass. +
      +
    • Seems like allowing 0 lines of indented code would suffice instead.
    • +
    +
  • +
  • I miss implicit return. +
      +
    • Explicit return looks a bit nicer, but is less concise, and I’ve gotten out of the habit.
    • +
    +
  • +
  • Mixins turn out to be more useful than multiple inheritance.
  • +
  • I miss string interpolation.
  • +
  • I really miss Array#first and Array#last.
  • +
  • I really don’t like that 0 is falsey. +
      +
    • I could live with empty lists and dicts being false, but not 0.0 and 0.
    • +
    +
  • +
  • Comparing a string to a regular expression seems harder than it should be.
  • +
  • Converting to a Boolean is not as easy a Ruby’s !! syntax. +
      +
    • OK, bool(expression) isn’t so bad, I guess.
    • +
    +
  • +
+

Where Python Rocks

+
    +
  • Indentation removes the need for end everywhere.
  • +
  • Import statements are nice. +
      +
    • Can import everything, or just a few things.
    • +
    +
  • +
  • Annotations are nice for aspect-oriented modification of methods. +
      +
    • Does it make it hard to debug though, like alias_method_chain in Ruby?
    • +
    • It’s kind of tricky to write them though, due to doubly-nested function definitions.
    • +
    +
  • +
  • List comprehensions are more powerful than map. +
      +
    • But map can be more concise for the most common cases.
    • +
    +
  • +
  • Can add arbitrary attributes to any object, class, or module.
  • +
  • The object creation syntax is nicely concise.
  • +
  • Sometimes the ability to add a bare function within a module is nice.
  • +
  • Keyword arguments are nicely done. +
      +
    • Ruby’s emulation via hashes without braces is OK, but the corner cases are problematic.
    • +
    +
  • +
  • Docstrings as part of the language is nice.
  • +
  • The new with keyword looks like a halfway-decent replacement for blocks. +
      +
    • Seems like a lot more work that using blocks and yield though.
    • +
    +
  • +
  • Python 3 drops support for octal literals starting with a 0. +
      +
    • Still allows it via a 0o prefix.
    • +
    +
  • +
  • No support for all the crazy Perl-inspired globals.
  • +
  • The names list and dictionary are better than array and hash. +
      +
    • The Ruby names are more about the implementation, especially hash.
    • +
    • I’d much prefer the name map over hash, or even dictionary.
    • +
    • The name list is only slightly better than array.
    • +
    +
  • +
  • List and string slicing is quite nice. +
      +
    • I do wish that the syntax was “..” instead of “:” though.
    • +
    • Slice assignment is even cooler.
    • +
    +
  • +
  • I prefer the Python dict syntax over the Ruby hash syntax (“:” vs. “=>”). +
      +
    • The Ruby 1.9 symbol hash syntax is an improvement, but not quite as good.
    • +
    +
  • +
  • Checking for string containment is nice: if substring in string.
  • +
+

Where Ruby Rocks

+
    +
  • Consistency.
  • +
  • Blocks.
  • +
  • Excellent functional tools to deal with Enumerable.
  • +
  • Meta-programming.
  • +
  • Optional parentheses.
  • +
  • Modules and classes are also objects.
  • +
+

 

+
+ + +
+ +
+
+ +

Debugging Pattern – Grenade

+ + + +
+

I’ve been more interested in programming patterns lately, partly due to the Ruby community’s recent interest — especially the Ruby Rogue podcast’s love affair with “Smalltalk Best Practice Patterns”.

+

I consider most of the patterns in “Smalltalk Best Practice Patterns” to be patterns “in the small” — things that are typically employed on a single line or method. The Gang Of Four patterns are more medium sized, dealing with methods and classes. The PEAA book covers architectural-scale patterns. I suppose “The Pragmatic Programmer” and similar books could (should!) be considered to be very general patterns, mostly about the practice of programming.

+

One type of pattern that I have not seen discussed much is debugging patterns. I’m not exactly sure why that is; probably just our general concentration on designing the program code itself. There are definitely testing patterns. But I can’t think of anything that I’ve seen that covers debugging patterns. A quick search on the Internet doesn’t turn up too much.

+

Anyway, a co-worker (Helena) and I were debugging recently, and were having trouble figuring out where a certain method on a certain object was being called. We came up with a really cool solution. We immediately called it a grenade, because its entire purpose is to blow up (raise an exception) and display a backtrace to show us the caller of the method that we’re looking for.

+

Here’s our implementation in Ruby:

+

+module Grenade
+  def method_under_investigation(*)
+    raise "method_under_investigation called"
+  end
+end
+
+class Xyz
+  ...
+  def xyz
+    object_under_investigation.extend(Grenade)
+  end
+  ...
+end
+

I’m sure we could wrap that in some more semantic sugar, even going as far as making it look something like this:

+

+class Xyz
+  ...
+  def xyz
+    object_under_investigation.blow_up_when_method_called(:method_under_investigation)
+  end
+  ...
+end
+
+

I’m not sure that would really be worth it though, unless we were to add it to some sort of library.

+

So that’s our contribution to the (small) list of debugging patterns.

+
+ + +
+ +
+
+ +

Write Comments For Yourself

+ + + +
+

Amos and I got in a heated discussion recently on whether we should write a single line comment to better explain some code. (The code in question was Amos’s very elegant solution to testing whether a job got sent to Resque.)

+

Amos doesn’t believe in writing comments much at all. He thinks that if you’re writing a comment, it means that you’re doing something wrong, and that you probably need to write the code more clearly.

+

I agree with that, to a point. First off, it’s not necessary to write perfect code. If you can change a class or method name to better describe what you’re doing (and more importantly, why you’re doing it) then you should definitely do so. But it’s not always worth refactoring until you get every “why” answered. More importantly, I don’t think it’s even possible to capture everything in the code that is worth capturing. For example, why did you choose this implementation, as opposed to another that might be more obvious or common?

+

After our argument, I came up with a good rule of thumb (or “pattern”):

+

Write comments for your (future) self.1

+

In other words, if your comment will help you to understand the code more quickly when you look at it in the future, then it’s a valid comment. It also means that you can assume that the reader has about as much general programming knowledge as you currently do. (Your future self will have more general knowledge, but possibly less specific knowledge of the lines of code in question. And because of this, your current solution might not make as much sense in the future. You might know of a better solution in the future, but you’ll have to know all the constraints that you had when you originally wrote the code.)

+

This is not to say that you should not write comments in clear English, that others can understand. The comment is written for a future maintainer. That may be you (which is why the rule works well), or it may be someone else. The rule is more about when to write a comment, and what level of competence you should assume of the reader.

+

1 Perhaps it should be “Write comments TO your (future) self”.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/programming/programming-languages.html b/public/category/programming/programming-languages.html new file mode 100644 index 0000000..d066e77 --- /dev/null +++ b/public/category/programming/programming-languages.html @@ -0,0 +1,591 @@ + + + + + + + + + + + + + + + +Languages – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

2015 Year in Review

+ + + +
+

It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+
+ + +
+ +
+
+ +

Not Quite Callbacks

+ + + +
+

I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+
+ + +
+ +
+
+ +

Architectural Thoughts

+ + + +
+

I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+
+ + +
+ +
+
+ +

Hexagonal Rails Controllers

+ + + +
+

I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+
+ + +
+ +
+
+ +

Resolutions

+ + + +
+

January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+
+ + +
+ +
+
+ +

Ruby Pattern: Parameterized Module Inclusion

+ + + +
+

I’ve run across a pattern in Ruby lately that I really like. It solves some problems that I’ve struggled with for several years. Let me start with the problems.

+

Let’s say you want to include an ORM in a model class, and want to tell it what database table to use. Typically, you’d do this:

+
class User
+  include MyORM::Model
+  table 'people'
+end
+

But that table call is more like an option to the module inclusion than anything else. So what we’d really like is something like this:

+
class User
+  include MyORM::Model, table: 'people'
+end
+

But that’s not valid Ruby; include doesn’t let you pass anything other than a module.

+

So when I was learning about Virtus, I noticed that its example of how to include it is a bit different than the standard Ruby idiomatic include:

+
class User
+  include Virtus.model
+end
+

At first glance, it reads like the first example. But on closer inspection and consideration, it’s quite a bit different. Where MyORM::Model is a constant that refers to a module, Virtus.model is a method call. So there’s a method named model in the Virtus module. That method returns another module — which is exactly what’s needed in order to include it into our model class.

+

The easiest way to implement Virtus.model would be this:

+
module Virtus
+  def model
+    ::Virtus::Model
+  end
+end
+
+module Virtus::Model
+  # ...
+end
+

If Virtus.model doesn’t need to take any arguments, that’s perfectly fine. In fact, I’ve started to use this implementation of the pattern for modules that don’t need parameters.

+

Because Virtus.model is a method, we can also call it with options:

+
class User
+  include Virtus.model(constructor: false, mass_assignment: false)
+end
+

We could even pass a block. But how do we process those options? There are a few different ways. However we do it, we have to be sure to return a module. And we can create modules in a few different ways.

+

Virtus uses the builder pattern. It takes the parameters passed in and builds a module dynamically. By that, I mean that it calls Module.new and then adds methods to that module. It does this by mixing in other modules, but it could do it by dynamically defining methods as well.

+

I’ve never seen this pattern in any other language. It’s obviously only possible because we can dynamically create modules.

+

The use of this idiom seems to be catching on a bit in the Ruby community. I’ve started using it myself, and will be adding it to my Includable::ActiveRecord gem soon.

+

 

+

 

+
+ + +
+ +
+
+ +

Brilliant – My Very Own Programming Language

+ + + +
+

I’ve decided to design and implement my own programming language, and call it Brilliant.

+

I’ve been interested in programming languages and linguistics almost as long as I’ve been using computers. I’ve long thought that if I ever go back to college, it’s likely that I’ll concentrate on programming languages as a specialty.

+

My recent discovery and involvement with the Crystal programming language has gotten me excited about new language ideas. It’s also helped me realize that implementing a language myself is feasible, and that there’s no better time to start than now.

+

Implementation

+

For now, the Brilliant implementation doesn’t let you do much more than “Hello World”. But you gotta start somewhere. So this article isn’t really “Introducing Brilliant” so much as the start of a series of articles on its design and implementation.

+

I wanted to use a PEG (parsing expression grammar) parser for several reasons. For one, they seem to have gained a lot of popularity in the past few years. Also, PEGs cannot be ambiguous, which can solve a few difficult problems, such as the “dangling else“. Perhaps my favorite feature of PEG grammars is that you don’t need a separate tokenizer (lexer). This provides a nice advantage in that we can use keywords like “class” as variables, as long as they’re not used in a place where the keyword would make sense.

+

So knowing that I wanted to use a PEG, I had to find a PEG parser. I kind of wanted to use ANTLR, which has been a leading parser for many years. But the PEG seems to be new to version 4, and I couldn’t find any Ruby bindings for version 4. Treetop seems to be the most popular parser for Ruby, but I found the EBNF format that Rattler uses to be more to my taste. I think the fact that it’s newer also gives it a few advantages, having had a chance to learn some lessons from Treetop.

+

I thought about using the Rubinius VM, but decided to go with LLVM, mainly since it has slightly better docs for Ruby, and because it’s what Crystal uses. Also, it’s pretty easy to get it to compile to a binary executable or run in a JIT. In the future, I might consider switching to the Rubinius VM, the Erlang VM, or the Perl 6 VM (Parrot). But for now, I like the idea of being able to compile to a binary and easily interface with C, just like Crystal.

+

Goals

+

My main goal is to have fun playing around with language ideas.

+

I’ve found a really great language in Ruby, so I’ll be using it as a starting point. But Ruby does have its faults. In some ways, I want to answer the question “what would Ruby look like if we designed it today?”.

+

But I also want to explore other ideas. What if objects defaulted to immutable? What if functions and methods were assumed to be pure by default? Might it be possible to infer the purity of a function or method? (If so, we could automatically memoize them.) Can we make creating an actor as easy as creating an object?

+

I’ll also be looking at ideas from other programming languages. Could we get some of the benefits of Haskell’s purity without having to do somersaults to do IO? Could we mix Python’s indentation style scoping with brace style or begin/end style? Could we integrate Icon’s ideas about success and failure (goal-directed execution)? What interesting features can we pull from Ada, Io, CoffeeScript, Crystal, Rubinius, Perl 6, etc.?
+

+

I’m not so much as interested in cutting-edge features, as in features that can be easily used by the average programmer. More importantly, I’m interested in how features interact with each other, so they fit well together to create a whole that’s greater than the sum of the parts.

+

Naming

+

I wanted to name my programming language “Fermat”. I’ve long been intrigued by Fermat’s Last Theorem, and Fermat’s Little Theorem is important in number theory. Unfortunately, there’s already a computer algebra system with that name.

+

So I decided to find a name in the same vein as “Ruby” and “Crystal”. I looked at the Wikipedia page for “gemstones” for some inspiration. A lot of the names of gemstones are already taken. I considered some obscure gemstones, but saw the word “brilliant” and thought it was decent. It’s not the name of another gemstone, but still evokes some similarity to Ruby and Crystal.

+

So that’s the name for now. Perhaps I’ll decide to change it at some point in the future, but I needed a name for the project, as well as a file extension for source code files. I chose “bril” for that. I suppose “br” would be a better choice. Perhaps I’ll change that before the next article in this series.

+

Future

+

I hope to work on Brilliant every once in a while. I expect it’ll take a couple years before it’s really very useful.

+

When I do add a major feature, I’ll be certain to blog about it. I’ve got tons of ideas strewn about in various files. It would be great to get them organized and published —and even better to get them implemented.

+
+ + +
+ +
+
+ +

Burying the Lede

+ + + +
+

Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+
+ + +
+ +
+
+ +

Yak Shaving #1: Cursor Keys

+ + + +
+

I recently decided to start using Emacs again. I used it extensively from the early 1990s until the early 2000s. I pretty much stopped using it when I had a sysadmin job with no Emacs on the servers, and no ability to install it. With the rising popularity of tmux and tmate for remote pairing, and my dislike for vim’s modes, I decided to try going back to Emacs in the terminal.

+

One thing I really want in a text editor is good cursor key support. Shifted cursor keys
+should select text, and Control+left and right should move by words. (Apple HIG says to use Option+left and right to move by words; most apps on Mac OS X seem to support both.) Things have worked this way on almost every text editor on every OS I’ve used — Amiga, DOS, Windows, NeXT, Mac, Motif, Gnome, KDE. It’s a part of the CUA standard that’s been in common usage on everything since the mid-1980s.

+

Enabling cursor keys in Emacs was pretty easy. I’ve decided to use Prelude to make getting started with Emacs easy. Emacs comes with the cursor keys enabled, but Prelude disables them. Undoing Prelude’s change is pretty easy:

+
(setq prelude-guru nil)
+

Trying to make shifted cursor keys work is where the trouble began. They work in the GUI version of Emacs, but not from the terminal. It turns out that the Mac Terminal doesn’t distinguish between cursor keys and shifted cursor keys in its default configuration. So I had to figure out how to configure key bindings in Terminal.

+

That’s easy enough — they’re in the preferences. But what should I set them to? This took a lot of research. Terminal emulation and ANSI code sequences are obscure, complex, and inconsistent. I eventually found the info I needed. For starters, Shift+Right, Shift+Left, Shift+Home, and Shift+End are defined in the terminfo. The rest I was able to piece together from various sources around the Internet.

+

I’m also trying to script my Mac configuration. So instead of manually adding all the keybindings in the Terminal preferences pane, I decided to write a script. Mac OS X does a decent job of allowing you to change preferences from the command line. For example, to always show the tab bar:

+
defaults write -app Terminal ShowTabBar 1
+

Easy enough, except for a couple problems. First, I had to figure out the obscure syntax used in the preferences for key codes. I was able to piece these together with some more Internet research. But the really big problem is that the keyboard bindings are 2 levels deep within a “dictionary” (hash map). And the defaults command doesn’t handle that. There are some obscure utilities that handle nested preferences, but they don’t work well with the preferences caching in Mac OS X 10.9 — a problem I ran into while testing.

+

So now I’m writing a utility in Python that does what the defaults command does, but that will handle nested dictionaries.

+

There’s a term for this process of having to solve one issue before you can solve another, and the issues get several layers deep. It’s called yak shaving.

+

Here’s another good example:

+
Father from Malcolm in the middle having to fix one thing before fixing another, ad infinitum.
Yak shaving – home repairs
+

I’m sure this will be the first of many posts about me yak shaving.

+
+ + +
+ +
+
+ +

Includable ActiveRecord

+ + + +
+

I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/programming/programming-languages/brilliant.html b/public/category/programming/programming-languages/brilliant.html new file mode 100644 index 0000000..bee43db --- /dev/null +++ b/public/category/programming/programming-languages/brilliant.html @@ -0,0 +1,303 @@ + + + + + + + + + + + + + + + +Brilliant – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

2015 Year in Review

+ + + +
+

It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+
+ + +
+ +
+
+ +

Resolutions

+ + + +
+

January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+
+ + +
+ +
+
+ +

Brilliant – My Very Own Programming Language

+ + + +
+

I’ve decided to design and implement my own programming language, and call it Brilliant.

+

I’ve been interested in programming languages and linguistics almost as long as I’ve been using computers. I’ve long thought that if I ever go back to college, it’s likely that I’ll concentrate on programming languages as a specialty.

+

My recent discovery and involvement with the Crystal programming language has gotten me excited about new language ideas. It’s also helped me realize that implementing a language myself is feasible, and that there’s no better time to start than now.

+

Implementation

+

For now, the Brilliant implementation doesn’t let you do much more than “Hello World”. But you gotta start somewhere. So this article isn’t really “Introducing Brilliant” so much as the start of a series of articles on its design and implementation.

+

I wanted to use a PEG (parsing expression grammar) parser for several reasons. For one, they seem to have gained a lot of popularity in the past few years. Also, PEGs cannot be ambiguous, which can solve a few difficult problems, such as the “dangling else“. Perhaps my favorite feature of PEG grammars is that you don’t need a separate tokenizer (lexer). This provides a nice advantage in that we can use keywords like “class” as variables, as long as they’re not used in a place where the keyword would make sense.

+

So knowing that I wanted to use a PEG, I had to find a PEG parser. I kind of wanted to use ANTLR, which has been a leading parser for many years. But the PEG seems to be new to version 4, and I couldn’t find any Ruby bindings for version 4. Treetop seems to be the most popular parser for Ruby, but I found the EBNF format that Rattler uses to be more to my taste. I think the fact that it’s newer also gives it a few advantages, having had a chance to learn some lessons from Treetop.

+

I thought about using the Rubinius VM, but decided to go with LLVM, mainly since it has slightly better docs for Ruby, and because it’s what Crystal uses. Also, it’s pretty easy to get it to compile to a binary executable or run in a JIT. In the future, I might consider switching to the Rubinius VM, the Erlang VM, or the Perl 6 VM (Parrot). But for now, I like the idea of being able to compile to a binary and easily interface with C, just like Crystal.

+

Goals

+

My main goal is to have fun playing around with language ideas.

+

I’ve found a really great language in Ruby, so I’ll be using it as a starting point. But Ruby does have its faults. In some ways, I want to answer the question “what would Ruby look like if we designed it today?”.

+

But I also want to explore other ideas. What if objects defaulted to immutable? What if functions and methods were assumed to be pure by default? Might it be possible to infer the purity of a function or method? (If so, we could automatically memoize them.) Can we make creating an actor as easy as creating an object?

+

I’ll also be looking at ideas from other programming languages. Could we get some of the benefits of Haskell’s purity without having to do somersaults to do IO? Could we mix Python’s indentation style scoping with brace style or begin/end style? Could we integrate Icon’s ideas about success and failure (goal-directed execution)? What interesting features can we pull from Ada, Io, CoffeeScript, Crystal, Rubinius, Perl 6, etc.?
+

+

I’m not so much as interested in cutting-edge features, as in features that can be easily used by the average programmer. More importantly, I’m interested in how features interact with each other, so they fit well together to create a whole that’s greater than the sum of the parts.

+

Naming

+

I wanted to name my programming language “Fermat”. I’ve long been intrigued by Fermat’s Last Theorem, and Fermat’s Little Theorem is important in number theory. Unfortunately, there’s already a computer algebra system with that name.

+

So I decided to find a name in the same vein as “Ruby” and “Crystal”. I looked at the Wikipedia page for “gemstones” for some inspiration. A lot of the names of gemstones are already taken. I considered some obscure gemstones, but saw the word “brilliant” and thought it was decent. It’s not the name of another gemstone, but still evokes some similarity to Ruby and Crystal.

+

So that’s the name for now. Perhaps I’ll decide to change it at some point in the future, but I needed a name for the project, as well as a file extension for source code files. I chose “bril” for that. I suppose “br” would be a better choice. Perhaps I’ll change that before the next article in this series.

+

Future

+

I hope to work on Brilliant every once in a while. I expect it’ll take a couple years before it’s really very useful.

+

When I do add a major feature, I’ll be certain to blog about it. I’ve got tons of ideas strewn about in various files. It would be great to get them organized and published —and even better to get them implemented.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/programming/programming-languages/brilliant/feed b/public/category/programming/programming-languages/brilliant/feed new file mode 100644 index 0000000..a661279 --- /dev/null +++ b/public/category/programming/programming-languages/brilliant/feed @@ -0,0 +1,143 @@ + + + + Brilliant – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + 2015 Year in Review + http://blog.boochtek.com/2015/12/28/year-in-review-2015 + http://blog.boochtek.com/2015/12/28/year-in-review-2015#respond + Tue, 29 Dec 2015 05:28:38 +0000 + + + + + + + + http://blog.boochtek.com/?p=287 + Continue reading "2015 Year in Review"]]> + It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+]]>
+ http://blog.boochtek.com/2015/12/28/year-in-review-2015/feed + 0 +
+ + Resolutions + http://blog.boochtek.com/2015/02/02/resolutions + http://blog.boochtek.com/2015/02/02/resolutions#respond + Tue, 03 Feb 2015 05:42:13 +0000 + + + + + + + + http://blog.boochtek.com/?p=198 + Continue reading "Resolutions"]]> + January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+]]>
+ http://blog.boochtek.com/2015/02/02/resolutions/feed + 0 +
+ + Brilliant – My Very Own Programming Language + http://blog.boochtek.com/2014/03/30/brilliant-my-own-programming-language + http://blog.boochtek.com/2014/03/30/brilliant-my-own-programming-language#comments + Mon, 31 Mar 2014 04:25:38 +0000 + + + + + http://blog.boochtek.com/?p=172 + Continue reading "Brilliant – My Very Own Programming Language"]]> + I’ve decided to design and implement my own programming language, and call it Brilliant.

+

I’ve been interested in programming languages and linguistics almost as long as I’ve been using computers. I’ve long thought that if I ever go back to college, it’s likely that I’ll concentrate on programming languages as a specialty.

+

My recent discovery and involvement with the Crystal programming language has gotten me excited about new language ideas. It’s also helped me realize that implementing a language myself is feasible, and that there’s no better time to start than now.

+

Implementation

+

For now, the Brilliant implementation doesn’t let you do much more than “Hello World”. But you gotta start somewhere. So this article isn’t really “Introducing Brilliant” so much as the start of a series of articles on its design and implementation.

+

I wanted to use a PEG (parsing expression grammar) parser for several reasons. For one, they seem to have gained a lot of popularity in the past few years. Also, PEGs cannot be ambiguous, which can solve a few difficult problems, such as the “dangling else“. Perhaps my favorite feature of PEG grammars is that you don’t need a separate tokenizer (lexer). This provides a nice advantage in that we can use keywords like “class” as variables, as long as they’re not used in a place where the keyword would make sense.

+

So knowing that I wanted to use a PEG, I had to find a PEG parser. I kind of wanted to use ANTLR, which has been a leading parser for many years. But the PEG seems to be new to version 4, and I couldn’t find any Ruby bindings for version 4. Treetop seems to be the most popular parser for Ruby, but I found the EBNF format that Rattler uses to be more to my taste. I think the fact that it’s newer also gives it a few advantages, having had a chance to learn some lessons from Treetop.

+

I thought about using the Rubinius VM, but decided to go with LLVM, mainly since it has slightly better docs for Ruby, and because it’s what Crystal uses. Also, it’s pretty easy to get it to compile to a binary executable or run in a JIT. In the future, I might consider switching to the Rubinius VM, the Erlang VM, or the Perl 6 VM (Parrot). But for now, I like the idea of being able to compile to a binary and easily interface with C, just like Crystal.

+

Goals

+

My main goal is to have fun playing around with language ideas.

+

I’ve found a really great language in Ruby, so I’ll be using it as a starting point. But Ruby does have its faults. In some ways, I want to answer the question “what would Ruby look like if we designed it today?”.

+

But I also want to explore other ideas. What if objects defaulted to immutable? What if functions and methods were assumed to be pure by default? Might it be possible to infer the purity of a function or method? (If so, we could automatically memoize them.) Can we make creating an actor as easy as creating an object?

+

I’ll also be looking at ideas from other programming languages. Could we get some of the benefits of Haskell’s purity without having to do somersaults to do IO? Could we mix Python’s indentation style scoping with brace style or begin/end style? Could we integrate Icon’s ideas about success and failure (goal-directed execution)? What interesting features can we pull from Ada, Io, CoffeeScript, Crystal, Rubinius, Perl 6, etc.?
+

+

I’m not so much as interested in cutting-edge features, as in features that can be easily used by the average programmer. More importantly, I’m interested in how features interact with each other, so they fit well together to create a whole that’s greater than the sum of the parts.

+

Naming

+

I wanted to name my programming language “Fermat”. I’ve long been intrigued by Fermat’s Last Theorem, and Fermat’s Little Theorem is important in number theory. Unfortunately, there’s already a computer algebra system with that name.

+

So I decided to find a name in the same vein as “Ruby” and “Crystal”. I looked at the Wikipedia page for “gemstones” for some inspiration. A lot of the names of gemstones are already taken. I considered some obscure gemstones, but saw the word “brilliant” and thought it was decent. It’s not the name of another gemstone, but still evokes some similarity to Ruby and Crystal.

+

So that’s the name for now. Perhaps I’ll decide to change it at some point in the future, but I needed a name for the project, as well as a file extension for source code files. I chose “bril” for that. I suppose “br” would be a better choice. Perhaps I’ll change that before the next article in this series.

+

Future

+

I hope to work on Brilliant every once in a while. I expect it’ll take a couple years before it’s really very useful.

+

When I do add a major feature, I’ll be certain to blog about it. I’ve got tons of ideas strewn about in various files. It would be great to get them organized and published —and even better to get them implemented.

+]]>
+ http://blog.boochtek.com/2014/03/30/brilliant-my-own-programming-language/feed + 1 +
+
+
diff --git a/public/category/programming/programming-languages/page/2.html b/public/category/programming/programming-languages/page/2.html new file mode 100644 index 0000000..edcdabc --- /dev/null +++ b/public/category/programming/programming-languages/page/2.html @@ -0,0 +1,416 @@ + + + + + + + + + + + + + + + +Languages – Page 2 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

My Thoughts on Python vs. Ruby

+ + + +
+

I’ve been using Python at work for the past few months.  I learned Python back in the early 2000s, but never used it for any large projects.  I learned Ruby in late 2005, and it quickly became my language of choice for most cases.

+

So while I still prefer Ruby, and will likely use Ruby more in the future than Python, I wanted to assess the strengths and weaknesses of Python in relation to Ruby.  Perhaps some of the lessons could be applied when writing Ruby, and it could help to decide when to use each.  Also, I’m interested in programming language design, and wanted to document pros and cons in that light.

+

Where Python Sucks (As Compared to Ruby)

+
    +
  • Have to explicitly include self in EVERY method declaration. +
      +
    • Including @classmethod declarations (although people usually use the name cls instead of self there).
    • +
    • Except @staticmethod declarations.
    • +
    +
  • +
  • Have to use self everywhere to reference an object’s own attributes.
  • +
  • Inconsistency of things like the len() function, when everything else is a method.
  • +
  • Inconsistency of having some built-in classes with lower-case names. +
      +
    • And they’re not whole words, so you have to memorize them: list, dict, str, int.
    • +
    • Even more bizarre is dict vs. OrderedDict.
    • +
    +
  • +
  • I’m not a fan of True and False being uppercase. +
      +
    • I’m a bit less concerned about None, for some reason.
    • +
    • If they’re going to be uppercase, they might as well be all caps, in my opinion.
    • +
    +
  • +
  • Inconsistency of camelCase versus snake_case in some modules.
  • +
  • Claims to prefer explicit over implicit, yet does not use new to create an object. +
      +
    • It is nicely concise, but makes it look too much like a regular function call.
    • +
    +
  • +
  • The implementation of lambdas is too limited. +
      +
    • Makes using map function not very useful.
    • +
    +
  • +
  • Class methods are less than ideal to implement. +
      +
    • Probably better to use functions within a module instead, in many cases.
    • +
    +
  • +
  • Lack of good functional programming tools makes it harder to manipulate lists. +
      +
    • Have to often resort to creating an initial list and adding to it in a for loop.
    • +
    +
  • +
  • I don’t understand why r’regex_string’ doesn’t just create an actual regular expression object.
  • +
  • I miss Ruby’s method-call-or-property-getter syntax. +
      +
    • Nice that I can get it by adding @property to method definitions, but that’s a bit messy.
    • +
    +
  • +
  • I don’t understand why lists don’t have a join() method; it seems backwards to call join on the string used to connect the list elements.
  • +
  • I miss unless; seems like with all the keywords, Python would have added that.
  • +
  • I really miss ||= to memoize. +
      +
    • The distinction between unset variables and variables set to None makes it hard.
    • +
    • I also miss +=.
    • +
    +
  • +
  • I really miss statement modifiers (if or unless at the end of the statement/expression instead of the beginning).
  • +
  • I miss being able to assign to a compound statement (x = if True: 1; else: 2). +
      +
    • While Python does allow this simple case, it does not allow more complex cases.
    • +
    +
  • +
  • The way modules, files, and classes work, I either have a lot of classes in one file, or have to come up with more module names in order to split classes into different files.
  • +
  • The preference for bare functions within modules over class methods often leads to functions outside of classes, when they’d make more sense more closely associated with the class.
  • +
  • I don’t quite understand the necessity for pass. +
      +
    • Seems like allowing 0 lines of indented code would suffice instead.
    • +
    +
  • +
  • I miss implicit return. +
      +
    • Explicit return looks a bit nicer, but is less concise, and I’ve gotten out of the habit.
    • +
    +
  • +
  • Mixins turn out to be more useful than multiple inheritance.
  • +
  • I miss string interpolation.
  • +
  • I really miss Array#first and Array#last.
  • +
  • I really don’t like that 0 is falsey. +
      +
    • I could live with empty lists and dicts being false, but not 0.0 and 0.
    • +
    +
  • +
  • Comparing a string to a regular expression seems harder than it should be.
  • +
  • Converting to a Boolean is not as easy a Ruby’s !! syntax. +
      +
    • OK, bool(expression) isn’t so bad, I guess.
    • +
    +
  • +
+

Where Python Rocks

+
    +
  • Indentation removes the need for end everywhere.
  • +
  • Import statements are nice. +
      +
    • Can import everything, or just a few things.
    • +
    +
  • +
  • Annotations are nice for aspect-oriented modification of methods. +
      +
    • Does it make it hard to debug though, like alias_method_chain in Ruby?
    • +
    • It’s kind of tricky to write them though, due to doubly-nested function definitions.
    • +
    +
  • +
  • List comprehensions are more powerful than map. +
      +
    • But map can be more concise for the most common cases.
    • +
    +
  • +
  • Can add arbitrary attributes to any object, class, or module.
  • +
  • The object creation syntax is nicely concise.
  • +
  • Sometimes the ability to add a bare function within a module is nice.
  • +
  • Keyword arguments are nicely done. +
      +
    • Ruby’s emulation via hashes without braces is OK, but the corner cases are problematic.
    • +
    +
  • +
  • Docstrings as part of the language is nice.
  • +
  • The new with keyword looks like a halfway-decent replacement for blocks. +
      +
    • Seems like a lot more work that using blocks and yield though.
    • +
    +
  • +
  • Python 3 drops support for octal literals starting with a 0. +
      +
    • Still allows it via a 0o prefix.
    • +
    +
  • +
  • No support for all the crazy Perl-inspired globals.
  • +
  • The names list and dictionary are better than array and hash. +
      +
    • The Ruby names are more about the implementation, especially hash.
    • +
    • I’d much prefer the name map over hash, or even dictionary.
    • +
    • The name list is only slightly better than array.
    • +
    +
  • +
  • List and string slicing is quite nice. +
      +
    • I do wish that the syntax was “..” instead of “:” though.
    • +
    • Slice assignment is even cooler.
    • +
    +
  • +
  • I prefer the Python dict syntax over the Ruby hash syntax (“:” vs. “=>”). +
      +
    • The Ruby 1.9 symbol hash syntax is an improvement, but not quite as good.
    • +
    +
  • +
  • Checking for string containment is nice: if substring in string.
  • +
+

Where Ruby Rocks

+
    +
  • Consistency.
  • +
  • Blocks.
  • +
  • Excellent functional tools to deal with Enumerable.
  • +
  • Meta-programming.
  • +
  • Optional parentheses.
  • +
  • Modules and classes are also objects.
  • +
+

 

+
+ + +
+ +
+
+ +

Debugging Pattern – Grenade

+ + + +
+

I’ve been more interested in programming patterns lately, partly due to the Ruby community’s recent interest — especially the Ruby Rogue podcast’s love affair with “Smalltalk Best Practice Patterns”.

+

I consider most of the patterns in “Smalltalk Best Practice Patterns” to be patterns “in the small” — things that are typically employed on a single line or method. The Gang Of Four patterns are more medium sized, dealing with methods and classes. The PEAA book covers architectural-scale patterns. I suppose “The Pragmatic Programmer” and similar books could (should!) be considered to be very general patterns, mostly about the practice of programming.

+

One type of pattern that I have not seen discussed much is debugging patterns. I’m not exactly sure why that is; probably just our general concentration on designing the program code itself. There are definitely testing patterns. But I can’t think of anything that I’ve seen that covers debugging patterns. A quick search on the Internet doesn’t turn up too much.

+

Anyway, a co-worker (Helena) and I were debugging recently, and were having trouble figuring out where a certain method on a certain object was being called. We came up with a really cool solution. We immediately called it a grenade, because its entire purpose is to blow up (raise an exception) and display a backtrace to show us the caller of the method that we’re looking for.

+

Here’s our implementation in Ruby:

+

+module Grenade
+  def method_under_investigation(*)
+    raise "method_under_investigation called"
+  end
+end
+
+class Xyz
+  ...
+  def xyz
+    object_under_investigation.extend(Grenade)
+  end
+  ...
+end
+

I’m sure we could wrap that in some more semantic sugar, even going as far as making it look something like this:

+

+class Xyz
+  ...
+  def xyz
+    object_under_investigation.blow_up_when_method_called(:method_under_investigation)
+  end
+  ...
+end
+
+

I’m not sure that would really be worth it though, unless we were to add it to some sort of library.

+

So that’s our contribution to the (small) list of debugging patterns.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/programming/programming-languages/python.html b/public/category/programming/programming-languages/python.html new file mode 100644 index 0000000..38ee225 --- /dev/null +++ b/public/category/programming/programming-languages/python.html @@ -0,0 +1,365 @@ + + + + + + + + + + + + + + + +Python – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

My Thoughts on Python vs. Ruby

+ + + +
+

I’ve been using Python at work for the past few months.  I learned Python back in the early 2000s, but never used it for any large projects.  I learned Ruby in late 2005, and it quickly became my language of choice for most cases.

+

So while I still prefer Ruby, and will likely use Ruby more in the future than Python, I wanted to assess the strengths and weaknesses of Python in relation to Ruby.  Perhaps some of the lessons could be applied when writing Ruby, and it could help to decide when to use each.  Also, I’m interested in programming language design, and wanted to document pros and cons in that light.

+

Where Python Sucks (As Compared to Ruby)

+
    +
  • Have to explicitly include self in EVERY method declaration. +
      +
    • Including @classmethod declarations (although people usually use the name cls instead of self there).
    • +
    • Except @staticmethod declarations.
    • +
    +
  • +
  • Have to use self everywhere to reference an object’s own attributes.
  • +
  • Inconsistency of things like the len() function, when everything else is a method.
  • +
  • Inconsistency of having some built-in classes with lower-case names. +
      +
    • And they’re not whole words, so you have to memorize them: list, dict, str, int.
    • +
    • Even more bizarre is dict vs. OrderedDict.
    • +
    +
  • +
  • I’m not a fan of True and False being uppercase. +
      +
    • I’m a bit less concerned about None, for some reason.
    • +
    • If they’re going to be uppercase, they might as well be all caps, in my opinion.
    • +
    +
  • +
  • Inconsistency of camelCase versus snake_case in some modules.
  • +
  • Claims to prefer explicit over implicit, yet does not use new to create an object. +
      +
    • It is nicely concise, but makes it look too much like a regular function call.
    • +
    +
  • +
  • The implementation of lambdas is too limited. +
      +
    • Makes using map function not very useful.
    • +
    +
  • +
  • Class methods are less than ideal to implement. +
      +
    • Probably better to use functions within a module instead, in many cases.
    • +
    +
  • +
  • Lack of good functional programming tools makes it harder to manipulate lists. +
      +
    • Have to often resort to creating an initial list and adding to it in a for loop.
    • +
    +
  • +
  • I don’t understand why r’regex_string’ doesn’t just create an actual regular expression object.
  • +
  • I miss Ruby’s method-call-or-property-getter syntax. +
      +
    • Nice that I can get it by adding @property to method definitions, but that’s a bit messy.
    • +
    +
  • +
  • I don’t understand why lists don’t have a join() method; it seems backwards to call join on the string used to connect the list elements.
  • +
  • I miss unless; seems like with all the keywords, Python would have added that.
  • +
  • I really miss ||= to memoize. +
      +
    • The distinction between unset variables and variables set to None makes it hard.
    • +
    • I also miss +=.
    • +
    +
  • +
  • I really miss statement modifiers (if or unless at the end of the statement/expression instead of the beginning).
  • +
  • I miss being able to assign to a compound statement (x = if True: 1; else: 2). +
      +
    • While Python does allow this simple case, it does not allow more complex cases.
    • +
    +
  • +
  • The way modules, files, and classes work, I either have a lot of classes in one file, or have to come up with more module names in order to split classes into different files.
  • +
  • The preference for bare functions within modules over class methods often leads to functions outside of classes, when they’d make more sense more closely associated with the class.
  • +
  • I don’t quite understand the necessity for pass. +
      +
    • Seems like allowing 0 lines of indented code would suffice instead.
    • +
    +
  • +
  • I miss implicit return. +
      +
    • Explicit return looks a bit nicer, but is less concise, and I’ve gotten out of the habit.
    • +
    +
  • +
  • Mixins turn out to be more useful than multiple inheritance.
  • +
  • I miss string interpolation.
  • +
  • I really miss Array#first and Array#last.
  • +
  • I really don’t like that 0 is falsey. +
      +
    • I could live with empty lists and dicts being false, but not 0.0 and 0.
    • +
    +
  • +
  • Comparing a string to a regular expression seems harder than it should be.
  • +
  • Converting to a Boolean is not as easy a Ruby’s !! syntax. +
      +
    • OK, bool(expression) isn’t so bad, I guess.
    • +
    +
  • +
+

Where Python Rocks

+
    +
  • Indentation removes the need for end everywhere.
  • +
  • Import statements are nice. +
      +
    • Can import everything, or just a few things.
    • +
    +
  • +
  • Annotations are nice for aspect-oriented modification of methods. +
      +
    • Does it make it hard to debug though, like alias_method_chain in Ruby?
    • +
    • It’s kind of tricky to write them though, due to doubly-nested function definitions.
    • +
    +
  • +
  • List comprehensions are more powerful than map. +
      +
    • But map can be more concise for the most common cases.
    • +
    +
  • +
  • Can add arbitrary attributes to any object, class, or module.
  • +
  • The object creation syntax is nicely concise.
  • +
  • Sometimes the ability to add a bare function within a module is nice.
  • +
  • Keyword arguments are nicely done. +
      +
    • Ruby’s emulation via hashes without braces is OK, but the corner cases are problematic.
    • +
    +
  • +
  • Docstrings as part of the language is nice.
  • +
  • The new with keyword looks like a halfway-decent replacement for blocks. +
      +
    • Seems like a lot more work that using blocks and yield though.
    • +
    +
  • +
  • Python 3 drops support for octal literals starting with a 0. +
      +
    • Still allows it via a 0o prefix.
    • +
    +
  • +
  • No support for all the crazy Perl-inspired globals.
  • +
  • The names list and dictionary are better than array and hash. +
      +
    • The Ruby names are more about the implementation, especially hash.
    • +
    • I’d much prefer the name map over hash, or even dictionary.
    • +
    • The name list is only slightly better than array.
    • +
    +
  • +
  • List and string slicing is quite nice. +
      +
    • I do wish that the syntax was “..” instead of “:” though.
    • +
    • Slice assignment is even cooler.
    • +
    +
  • +
  • I prefer the Python dict syntax over the Ruby hash syntax (“:” vs. “=>”). +
      +
    • The Ruby 1.9 symbol hash syntax is an improvement, but not quite as good.
    • +
    +
  • +
  • Checking for string containment is nice: if substring in string.
  • +
+

Where Ruby Rocks

+
    +
  • Consistency.
  • +
  • Blocks.
  • +
  • Excellent functional tools to deal with Enumerable.
  • +
  • Meta-programming.
  • +
  • Optional parentheses.
  • +
  • Modules and classes are also objects.
  • +
+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/programming/programming-languages/python/feed b/public/category/programming/programming-languages/python/feed new file mode 100644 index 0000000..5718107 --- /dev/null +++ b/public/category/programming/programming-languages/python/feed @@ -0,0 +1,198 @@ + + + + Python – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + My Thoughts on Python vs. Ruby + http://blog.boochtek.com/2013/02/22/my-thoughts-on-python-vs-ruby + http://blog.boochtek.com/2013/02/22/my-thoughts-on-python-vs-ruby#comments + Fri, 22 Feb 2013 17:03:06 +0000 + + + + + + http://blog.boochtek.com/?p=76 + Continue reading "My Thoughts on Python vs. Ruby"]]> + I’ve been using Python at work for the past few months.  I learned Python back in the early 2000s, but never used it for any large projects.  I learned Ruby in late 2005, and it quickly became my language of choice for most cases.

+

So while I still prefer Ruby, and will likely use Ruby more in the future than Python, I wanted to assess the strengths and weaknesses of Python in relation to Ruby.  Perhaps some of the lessons could be applied when writing Ruby, and it could help to decide when to use each.  Also, I’m interested in programming language design, and wanted to document pros and cons in that light.

+

Where Python Sucks (As Compared to Ruby)

+
    +
  • Have to explicitly include self in EVERY method declaration. +
      +
    • Including @classmethod declarations (although people usually use the name cls instead of self there).
    • +
    • Except @staticmethod declarations.
    • +
    +
  • +
  • Have to use self everywhere to reference an object’s own attributes.
  • +
  • Inconsistency of things like the len() function, when everything else is a method.
  • +
  • Inconsistency of having some built-in classes with lower-case names. +
      +
    • And they’re not whole words, so you have to memorize them: list, dict, str, int.
    • +
    • Even more bizarre is dict vs. OrderedDict.
    • +
    +
  • +
  • I’m not a fan of True and False being uppercase. +
      +
    • I’m a bit less concerned about None, for some reason.
    • +
    • If they’re going to be uppercase, they might as well be all caps, in my opinion.
    • +
    +
  • +
  • Inconsistency of camelCase versus snake_case in some modules.
  • +
  • Claims to prefer explicit over implicit, yet does not use new to create an object. +
      +
    • It is nicely concise, but makes it look too much like a regular function call.
    • +
    +
  • +
  • The implementation of lambdas is too limited. +
      +
    • Makes using map function not very useful.
    • +
    +
  • +
  • Class methods are less than ideal to implement. +
      +
    • Probably better to use functions within a module instead, in many cases.
    • +
    +
  • +
  • Lack of good functional programming tools makes it harder to manipulate lists. +
      +
    • Have to often resort to creating an initial list and adding to it in a for loop.
    • +
    +
  • +
  • I don’t understand why r’regex_string’ doesn’t just create an actual regular expression object.
  • +
  • I miss Ruby’s method-call-or-property-getter syntax. +
      +
    • Nice that I can get it by adding @property to method definitions, but that’s a bit messy.
    • +
    +
  • +
  • I don’t understand why lists don’t have a join() method; it seems backwards to call join on the string used to connect the list elements.
  • +
  • I miss unless; seems like with all the keywords, Python would have added that.
  • +
  • I really miss ||= to memoize. +
      +
    • The distinction between unset variables and variables set to None makes it hard.
    • +
    • I also miss +=.
    • +
    +
  • +
  • I really miss statement modifiers (if or unless at the end of the statement/expression instead of the beginning).
  • +
  • I miss being able to assign to a compound statement (x = if True: 1; else: 2). +
      +
    • While Python does allow this simple case, it does not allow more complex cases.
    • +
    +
  • +
  • The way modules, files, and classes work, I either have a lot of classes in one file, or have to come up with more module names in order to split classes into different files.
  • +
  • The preference for bare functions within modules over class methods often leads to functions outside of classes, when they’d make more sense more closely associated with the class.
  • +
  • I don’t quite understand the necessity for pass. +
      +
    • Seems like allowing 0 lines of indented code would suffice instead.
    • +
    +
  • +
  • I miss implicit return. +
      +
    • Explicit return looks a bit nicer, but is less concise, and I’ve gotten out of the habit.
    • +
    +
  • +
  • Mixins turn out to be more useful than multiple inheritance.
  • +
  • I miss string interpolation.
  • +
  • I really miss Array#first and Array#last.
  • +
  • I really don’t like that 0 is falsey. +
      +
    • I could live with empty lists and dicts being false, but not 0.0 and 0.
    • +
    +
  • +
  • Comparing a string to a regular expression seems harder than it should be.
  • +
  • Converting to a Boolean is not as easy a Ruby’s !! syntax. +
      +
    • OK, bool(expression) isn’t so bad, I guess.
    • +
    +
  • +
+

Where Python Rocks

+
    +
  • Indentation removes the need for end everywhere.
  • +
  • Import statements are nice. +
      +
    • Can import everything, or just a few things.
    • +
    +
  • +
  • Annotations are nice for aspect-oriented modification of methods. +
      +
    • Does it make it hard to debug though, like alias_method_chain in Ruby?
    • +
    • It’s kind of tricky to write them though, due to doubly-nested function definitions.
    • +
    +
  • +
  • List comprehensions are more powerful than map. +
      +
    • But map can be more concise for the most common cases.
    • +
    +
  • +
  • Can add arbitrary attributes to any object, class, or module.
  • +
  • The object creation syntax is nicely concise.
  • +
  • Sometimes the ability to add a bare function within a module is nice.
  • +
  • Keyword arguments are nicely done. +
      +
    • Ruby’s emulation via hashes without braces is OK, but the corner cases are problematic.
    • +
    +
  • +
  • Docstrings as part of the language is nice.
  • +
  • The new with keyword looks like a halfway-decent replacement for blocks. +
      +
    • Seems like a lot more work that using blocks and yield though.
    • +
    +
  • +
  • Python 3 drops support for octal literals starting with a 0. +
      +
    • Still allows it via a 0o prefix.
    • +
    +
  • +
  • No support for all the crazy Perl-inspired globals.
  • +
  • The names list and dictionary are better than array and hash. +
      +
    • The Ruby names are more about the implementation, especially hash.
    • +
    • I’d much prefer the name map over hash, or even dictionary.
    • +
    • The name list is only slightly better than array.
    • +
    +
  • +
  • List and string slicing is quite nice. +
      +
    • I do wish that the syntax was “..” instead of “:” though.
    • +
    • Slice assignment is even cooler.
    • +
    +
  • +
  • I prefer the Python dict syntax over the Ruby hash syntax (“:” vs. “=>”). +
      +
    • The Ruby 1.9 symbol hash syntax is an improvement, but not quite as good.
    • +
    +
  • +
  • Checking for string containment is nice: if substring in string.
  • +
+

Where Ruby Rocks

+
    +
  • Consistency.
  • +
  • Blocks.
  • +
  • Excellent functional tools to deal with Enumerable.
  • +
  • Meta-programming.
  • +
  • Optional parentheses.
  • +
  • Modules and classes are also objects.
  • +
+

 

+]]>
+ http://blog.boochtek.com/2013/02/22/my-thoughts-on-python-vs-ruby/feed + 1 +
+
+
diff --git a/public/category/programming/programming-languages/ruby.html b/public/category/programming/programming-languages/ruby.html new file mode 100644 index 0000000..37558aa --- /dev/null +++ b/public/category/programming/programming-languages/ruby.html @@ -0,0 +1,625 @@ + + + + + + + + + + + + + + + +Ruby – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Not Quite Callbacks

+ + + +
+

I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+
+ + +
+ +
+
+ +

Architectural Thoughts

+ + + +
+

I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+
+ + +
+ +
+
+ +

Hexagonal Rails Controllers

+ + + +
+

I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+
+ + +
+ +
+
+ +

Ruby Pattern: Parameterized Module Inclusion

+ + + +
+

I’ve run across a pattern in Ruby lately that I really like. It solves some problems that I’ve struggled with for several years. Let me start with the problems.

+

Let’s say you want to include an ORM in a model class, and want to tell it what database table to use. Typically, you’d do this:

+
class User
+  include MyORM::Model
+  table 'people'
+end
+

But that table call is more like an option to the module inclusion than anything else. So what we’d really like is something like this:

+
class User
+  include MyORM::Model, table: 'people'
+end
+

But that’s not valid Ruby; include doesn’t let you pass anything other than a module.

+

So when I was learning about Virtus, I noticed that its example of how to include it is a bit different than the standard Ruby idiomatic include:

+
class User
+  include Virtus.model
+end
+

At first glance, it reads like the first example. But on closer inspection and consideration, it’s quite a bit different. Where MyORM::Model is a constant that refers to a module, Virtus.model is a method call. So there’s a method named model in the Virtus module. That method returns another module — which is exactly what’s needed in order to include it into our model class.

+

The easiest way to implement Virtus.model would be this:

+
module Virtus
+  def model
+    ::Virtus::Model
+  end
+end
+
+module Virtus::Model
+  # ...
+end
+

If Virtus.model doesn’t need to take any arguments, that’s perfectly fine. In fact, I’ve started to use this implementation of the pattern for modules that don’t need parameters.

+

Because Virtus.model is a method, we can also call it with options:

+
class User
+  include Virtus.model(constructor: false, mass_assignment: false)
+end
+

We could even pass a block. But how do we process those options? There are a few different ways. However we do it, we have to be sure to return a module. And we can create modules in a few different ways.

+

Virtus uses the builder pattern. It takes the parameters passed in and builds a module dynamically. By that, I mean that it calls Module.new and then adds methods to that module. It does this by mixing in other modules, but it could do it by dynamically defining methods as well.

+

I’ve never seen this pattern in any other language. It’s obviously only possible because we can dynamically create modules.

+

The use of this idiom seems to be catching on a bit in the Ruby community. I’ve started using it myself, and will be adding it to my Includable::ActiveRecord gem soon.

+

 

+

 

+
+ + +
+ +
+
+ +

Includable ActiveRecord

+ + + +
+

I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+
+ + +
+ +
+
+ +

My Thoughts on Python vs. Ruby

+ + + +
+

I’ve been using Python at work for the past few months.  I learned Python back in the early 2000s, but never used it for any large projects.  I learned Ruby in late 2005, and it quickly became my language of choice for most cases.

+

So while I still prefer Ruby, and will likely use Ruby more in the future than Python, I wanted to assess the strengths and weaknesses of Python in relation to Ruby.  Perhaps some of the lessons could be applied when writing Ruby, and it could help to decide when to use each.  Also, I’m interested in programming language design, and wanted to document pros and cons in that light.

+

Where Python Sucks (As Compared to Ruby)

+
    +
  • Have to explicitly include self in EVERY method declaration. +
      +
    • Including @classmethod declarations (although people usually use the name cls instead of self there).
    • +
    • Except @staticmethod declarations.
    • +
    +
  • +
  • Have to use self everywhere to reference an object’s own attributes.
  • +
  • Inconsistency of things like the len() function, when everything else is a method.
  • +
  • Inconsistency of having some built-in classes with lower-case names. +
      +
    • And they’re not whole words, so you have to memorize them: list, dict, str, int.
    • +
    • Even more bizarre is dict vs. OrderedDict.
    • +
    +
  • +
  • I’m not a fan of True and False being uppercase. +
      +
    • I’m a bit less concerned about None, for some reason.
    • +
    • If they’re going to be uppercase, they might as well be all caps, in my opinion.
    • +
    +
  • +
  • Inconsistency of camelCase versus snake_case in some modules.
  • +
  • Claims to prefer explicit over implicit, yet does not use new to create an object. +
      +
    • It is nicely concise, but makes it look too much like a regular function call.
    • +
    +
  • +
  • The implementation of lambdas is too limited. +
      +
    • Makes using map function not very useful.
    • +
    +
  • +
  • Class methods are less than ideal to implement. +
      +
    • Probably better to use functions within a module instead, in many cases.
    • +
    +
  • +
  • Lack of good functional programming tools makes it harder to manipulate lists. +
      +
    • Have to often resort to creating an initial list and adding to it in a for loop.
    • +
    +
  • +
  • I don’t understand why r’regex_string’ doesn’t just create an actual regular expression object.
  • +
  • I miss Ruby’s method-call-or-property-getter syntax. +
      +
    • Nice that I can get it by adding @property to method definitions, but that’s a bit messy.
    • +
    +
  • +
  • I don’t understand why lists don’t have a join() method; it seems backwards to call join on the string used to connect the list elements.
  • +
  • I miss unless; seems like with all the keywords, Python would have added that.
  • +
  • I really miss ||= to memoize. +
      +
    • The distinction between unset variables and variables set to None makes it hard.
    • +
    • I also miss +=.
    • +
    +
  • +
  • I really miss statement modifiers (if or unless at the end of the statement/expression instead of the beginning).
  • +
  • I miss being able to assign to a compound statement (x = if True: 1; else: 2). +
      +
    • While Python does allow this simple case, it does not allow more complex cases.
    • +
    +
  • +
  • The way modules, files, and classes work, I either have a lot of classes in one file, or have to come up with more module names in order to split classes into different files.
  • +
  • The preference for bare functions within modules over class methods often leads to functions outside of classes, when they’d make more sense more closely associated with the class.
  • +
  • I don’t quite understand the necessity for pass. +
      +
    • Seems like allowing 0 lines of indented code would suffice instead.
    • +
    +
  • +
  • I miss implicit return. +
      +
    • Explicit return looks a bit nicer, but is less concise, and I’ve gotten out of the habit.
    • +
    +
  • +
  • Mixins turn out to be more useful than multiple inheritance.
  • +
  • I miss string interpolation.
  • +
  • I really miss Array#first and Array#last.
  • +
  • I really don’t like that 0 is falsey. +
      +
    • I could live with empty lists and dicts being false, but not 0.0 and 0.
    • +
    +
  • +
  • Comparing a string to a regular expression seems harder than it should be.
  • +
  • Converting to a Boolean is not as easy a Ruby’s !! syntax. +
      +
    • OK, bool(expression) isn’t so bad, I guess.
    • +
    +
  • +
+

Where Python Rocks

+
    +
  • Indentation removes the need for end everywhere.
  • +
  • Import statements are nice. +
      +
    • Can import everything, or just a few things.
    • +
    +
  • +
  • Annotations are nice for aspect-oriented modification of methods. +
      +
    • Does it make it hard to debug though, like alias_method_chain in Ruby?
    • +
    • It’s kind of tricky to write them though, due to doubly-nested function definitions.
    • +
    +
  • +
  • List comprehensions are more powerful than map. +
      +
    • But map can be more concise for the most common cases.
    • +
    +
  • +
  • Can add arbitrary attributes to any object, class, or module.
  • +
  • The object creation syntax is nicely concise.
  • +
  • Sometimes the ability to add a bare function within a module is nice.
  • +
  • Keyword arguments are nicely done. +
      +
    • Ruby’s emulation via hashes without braces is OK, but the corner cases are problematic.
    • +
    +
  • +
  • Docstrings as part of the language is nice.
  • +
  • The new with keyword looks like a halfway-decent replacement for blocks. +
      +
    • Seems like a lot more work that using blocks and yield though.
    • +
    +
  • +
  • Python 3 drops support for octal literals starting with a 0. +
      +
    • Still allows it via a 0o prefix.
    • +
    +
  • +
  • No support for all the crazy Perl-inspired globals.
  • +
  • The names list and dictionary are better than array and hash. +
      +
    • The Ruby names are more about the implementation, especially hash.
    • +
    • I’d much prefer the name map over hash, or even dictionary.
    • +
    • The name list is only slightly better than array.
    • +
    +
  • +
  • List and string slicing is quite nice. +
      +
    • I do wish that the syntax was “..” instead of “:” though.
    • +
    • Slice assignment is even cooler.
    • +
    +
  • +
  • I prefer the Python dict syntax over the Ruby hash syntax (“:” vs. “=>”). +
      +
    • The Ruby 1.9 symbol hash syntax is an improvement, but not quite as good.
    • +
    +
  • +
  • Checking for string containment is nice: if substring in string.
  • +
+

Where Ruby Rocks

+
    +
  • Consistency.
  • +
  • Blocks.
  • +
  • Excellent functional tools to deal with Enumerable.
  • +
  • Meta-programming.
  • +
  • Optional parentheses.
  • +
  • Modules and classes are also objects.
  • +
+

 

+
+ + +
+ +
+
+ +

Debugging Pattern – Grenade

+ + + +
+

I’ve been more interested in programming patterns lately, partly due to the Ruby community’s recent interest — especially the Ruby Rogue podcast’s love affair with “Smalltalk Best Practice Patterns”.

+

I consider most of the patterns in “Smalltalk Best Practice Patterns” to be patterns “in the small” — things that are typically employed on a single line or method. The Gang Of Four patterns are more medium sized, dealing with methods and classes. The PEAA book covers architectural-scale patterns. I suppose “The Pragmatic Programmer” and similar books could (should!) be considered to be very general patterns, mostly about the practice of programming.

+

One type of pattern that I have not seen discussed much is debugging patterns. I’m not exactly sure why that is; probably just our general concentration on designing the program code itself. There are definitely testing patterns. But I can’t think of anything that I’ve seen that covers debugging patterns. A quick search on the Internet doesn’t turn up too much.

+

Anyway, a co-worker (Helena) and I were debugging recently, and were having trouble figuring out where a certain method on a certain object was being called. We came up with a really cool solution. We immediately called it a grenade, because its entire purpose is to blow up (raise an exception) and display a backtrace to show us the caller of the method that we’re looking for.

+

Here’s our implementation in Ruby:

+

+module Grenade
+  def method_under_investigation(*)
+    raise "method_under_investigation called"
+  end
+end
+
+class Xyz
+  ...
+  def xyz
+    object_under_investigation.extend(Grenade)
+  end
+  ...
+end
+

I’m sure we could wrap that in some more semantic sugar, even going as far as making it look something like this:

+

+class Xyz
+  ...
+  def xyz
+    object_under_investigation.blow_up_when_method_called(:method_under_investigation)
+  end
+  ...
+end
+
+

I’m not sure that would really be worth it though, unless we were to add it to some sort of library.

+

So that’s our contribution to the (small) list of debugging patterns.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/programming/programming-languages/ruby/feed b/public/category/programming/programming-languages/ruby/feed new file mode 100644 index 0000000..1a2198c --- /dev/null +++ b/public/category/programming/programming-languages/ruby/feed @@ -0,0 +1,468 @@ + + + + Ruby – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Not Quite Callbacks + http://blog.boochtek.com/2015/06/22/not-quite-callbacks + http://blog.boochtek.com/2015/06/22/not-quite-callbacks#respond + Tue, 23 Jun 2015 04:55:17 +0000 + + + + + + http://blog.boochtek.com/?p=245 + Continue reading "Not Quite Callbacks"]]> + I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+]]>
+ http://blog.boochtek.com/2015/06/22/not-quite-callbacks/feed + 0 +
+ + Architectural Thoughts + http://blog.boochtek.com/2015/06/01/architectural-thoughts + http://blog.boochtek.com/2015/06/01/architectural-thoughts#respond + Tue, 02 Jun 2015 04:51:59 +0000 + + + + + + + http://blog.boochtek.com/?p=240 + Continue reading "Architectural Thoughts"]]> + I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+]]>
+ http://blog.boochtek.com/2015/06/01/architectural-thoughts/feed + 0 +
+ + Hexagonal Rails Controllers + http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers + http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers#respond + Tue, 24 Feb 2015 05:50:28 +0000 + + + + + + http://blog.boochtek.com/?p=202 + Continue reading "Hexagonal Rails Controllers"]]> + I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+]]>
+ http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers/feed + 0 +
+ + Ruby Pattern: Parameterized Module Inclusion + http://blog.boochtek.com/2014/04/14/ruby-parameterized-module-inclusion + http://blog.boochtek.com/2014/04/14/ruby-parameterized-module-inclusion#comments + Tue, 15 Apr 2014 04:57:29 +0000 + + + + http://blog.boochtek.com/?p=184 + Continue reading "Ruby Pattern: Parameterized Module Inclusion"]]> + I’ve run across a pattern in Ruby lately that I really like. It solves some problems that I’ve struggled with for several years. Let me start with the problems.

+

Let’s say you want to include an ORM in a model class, and want to tell it what database table to use. Typically, you’d do this:

+
class User
+  include MyORM::Model
+  table 'people'
+end
+

But that table call is more like an option to the module inclusion than anything else. So what we’d really like is something like this:

+
class User
+  include MyORM::Model, table: 'people'
+end
+

But that’s not valid Ruby; include doesn’t let you pass anything other than a module.

+

So when I was learning about Virtus, I noticed that its example of how to include it is a bit different than the standard Ruby idiomatic include:

+
class User
+  include Virtus.model
+end
+

At first glance, it reads like the first example. But on closer inspection and consideration, it’s quite a bit different. Where MyORM::Model is a constant that refers to a module, Virtus.model is a method call. So there’s a method named model in the Virtus module. That method returns another module — which is exactly what’s needed in order to include it into our model class.

+

The easiest way to implement Virtus.model would be this:

+
module Virtus
+  def model
+    ::Virtus::Model
+  end
+end
+
+module Virtus::Model
+  # ...
+end
+

If Virtus.model doesn’t need to take any arguments, that’s perfectly fine. In fact, I’ve started to use this implementation of the pattern for modules that don’t need parameters.

+

Because Virtus.model is a method, we can also call it with options:

+
class User
+  include Virtus.model(constructor: false, mass_assignment: false)
+end
+

We could even pass a block. But how do we process those options? There are a few different ways. However we do it, we have to be sure to return a module. And we can create modules in a few different ways.

+

Virtus uses the builder pattern. It takes the parameters passed in and builds a module dynamically. By that, I mean that it calls Module.new and then adds methods to that module. It does this by mixing in other modules, but it could do it by dynamically defining methods as well.

+

I’ve never seen this pattern in any other language. It’s obviously only possible because we can dynamically create modules.

+

The use of this idiom seems to be catching on a bit in the Ruby community. I’ve started using it myself, and will be adding it to my Includable::ActiveRecord gem soon.

+

 

+

 

+]]>
+ http://blog.boochtek.com/2014/04/14/ruby-parameterized-module-inclusion/feed + 2 +
+ + Includable ActiveRecord + http://blog.boochtek.com/2014/02/10/includable-activerecord + http://blog.boochtek.com/2014/02/10/includable-activerecord#respond + Mon, 10 Feb 2014 18:09:53 +0000 + + + + + + http://blog.boochtek.com/?p=104 + Continue reading "Includable ActiveRecord"]]> + I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+]]>
+ http://blog.boochtek.com/2014/02/10/includable-activerecord/feed + 0 +
+ + My Thoughts on Python vs. Ruby + http://blog.boochtek.com/2013/02/22/my-thoughts-on-python-vs-ruby + http://blog.boochtek.com/2013/02/22/my-thoughts-on-python-vs-ruby#comments + Fri, 22 Feb 2013 17:03:06 +0000 + + + + + + http://blog.boochtek.com/?p=76 + Continue reading "My Thoughts on Python vs. Ruby"]]> + I’ve been using Python at work for the past few months.  I learned Python back in the early 2000s, but never used it for any large projects.  I learned Ruby in late 2005, and it quickly became my language of choice for most cases.

+

So while I still prefer Ruby, and will likely use Ruby more in the future than Python, I wanted to assess the strengths and weaknesses of Python in relation to Ruby.  Perhaps some of the lessons could be applied when writing Ruby, and it could help to decide when to use each.  Also, I’m interested in programming language design, and wanted to document pros and cons in that light.

+

Where Python Sucks (As Compared to Ruby)

+
    +
  • Have to explicitly include self in EVERY method declaration. +
      +
    • Including @classmethod declarations (although people usually use the name cls instead of self there).
    • +
    • Except @staticmethod declarations.
    • +
    +
  • +
  • Have to use self everywhere to reference an object’s own attributes.
  • +
  • Inconsistency of things like the len() function, when everything else is a method.
  • +
  • Inconsistency of having some built-in classes with lower-case names. +
      +
    • And they’re not whole words, so you have to memorize them: list, dict, str, int.
    • +
    • Even more bizarre is dict vs. OrderedDict.
    • +
    +
  • +
  • I’m not a fan of True and False being uppercase. +
      +
    • I’m a bit less concerned about None, for some reason.
    • +
    • If they’re going to be uppercase, they might as well be all caps, in my opinion.
    • +
    +
  • +
  • Inconsistency of camelCase versus snake_case in some modules.
  • +
  • Claims to prefer explicit over implicit, yet does not use new to create an object. +
      +
    • It is nicely concise, but makes it look too much like a regular function call.
    • +
    +
  • +
  • The implementation of lambdas is too limited. +
      +
    • Makes using map function not very useful.
    • +
    +
  • +
  • Class methods are less than ideal to implement. +
      +
    • Probably better to use functions within a module instead, in many cases.
    • +
    +
  • +
  • Lack of good functional programming tools makes it harder to manipulate lists. +
      +
    • Have to often resort to creating an initial list and adding to it in a for loop.
    • +
    +
  • +
  • I don’t understand why r’regex_string’ doesn’t just create an actual regular expression object.
  • +
  • I miss Ruby’s method-call-or-property-getter syntax. +
      +
    • Nice that I can get it by adding @property to method definitions, but that’s a bit messy.
    • +
    +
  • +
  • I don’t understand why lists don’t have a join() method; it seems backwards to call join on the string used to connect the list elements.
  • +
  • I miss unless; seems like with all the keywords, Python would have added that.
  • +
  • I really miss ||= to memoize. +
      +
    • The distinction between unset variables and variables set to None makes it hard.
    • +
    • I also miss +=.
    • +
    +
  • +
  • I really miss statement modifiers (if or unless at the end of the statement/expression instead of the beginning).
  • +
  • I miss being able to assign to a compound statement (x = if True: 1; else: 2). +
      +
    • While Python does allow this simple case, it does not allow more complex cases.
    • +
    +
  • +
  • The way modules, files, and classes work, I either have a lot of classes in one file, or have to come up with more module names in order to split classes into different files.
  • +
  • The preference for bare functions within modules over class methods often leads to functions outside of classes, when they’d make more sense more closely associated with the class.
  • +
  • I don’t quite understand the necessity for pass. +
      +
    • Seems like allowing 0 lines of indented code would suffice instead.
    • +
    +
  • +
  • I miss implicit return. +
      +
    • Explicit return looks a bit nicer, but is less concise, and I’ve gotten out of the habit.
    • +
    +
  • +
  • Mixins turn out to be more useful than multiple inheritance.
  • +
  • I miss string interpolation.
  • +
  • I really miss Array#first and Array#last.
  • +
  • I really don’t like that 0 is falsey. +
      +
    • I could live with empty lists and dicts being false, but not 0.0 and 0.
    • +
    +
  • +
  • Comparing a string to a regular expression seems harder than it should be.
  • +
  • Converting to a Boolean is not as easy a Ruby’s !! syntax. +
      +
    • OK, bool(expression) isn’t so bad, I guess.
    • +
    +
  • +
+

Where Python Rocks

+
    +
  • Indentation removes the need for end everywhere.
  • +
  • Import statements are nice. +
      +
    • Can import everything, or just a few things.
    • +
    +
  • +
  • Annotations are nice for aspect-oriented modification of methods. +
      +
    • Does it make it hard to debug though, like alias_method_chain in Ruby?
    • +
    • It’s kind of tricky to write them though, due to doubly-nested function definitions.
    • +
    +
  • +
  • List comprehensions are more powerful than map. +
      +
    • But map can be more concise for the most common cases.
    • +
    +
  • +
  • Can add arbitrary attributes to any object, class, or module.
  • +
  • The object creation syntax is nicely concise.
  • +
  • Sometimes the ability to add a bare function within a module is nice.
  • +
  • Keyword arguments are nicely done. +
      +
    • Ruby’s emulation via hashes without braces is OK, but the corner cases are problematic.
    • +
    +
  • +
  • Docstrings as part of the language is nice.
  • +
  • The new with keyword looks like a halfway-decent replacement for blocks. +
      +
    • Seems like a lot more work that using blocks and yield though.
    • +
    +
  • +
  • Python 3 drops support for octal literals starting with a 0. +
      +
    • Still allows it via a 0o prefix.
    • +
    +
  • +
  • No support for all the crazy Perl-inspired globals.
  • +
  • The names list and dictionary are better than array and hash. +
      +
    • The Ruby names are more about the implementation, especially hash.
    • +
    • I’d much prefer the name map over hash, or even dictionary.
    • +
    • The name list is only slightly better than array.
    • +
    +
  • +
  • List and string slicing is quite nice. +
      +
    • I do wish that the syntax was “..” instead of “:” though.
    • +
    • Slice assignment is even cooler.
    • +
    +
  • +
  • I prefer the Python dict syntax over the Ruby hash syntax (“:” vs. “=>”). +
      +
    • The Ruby 1.9 symbol hash syntax is an improvement, but not quite as good.
    • +
    +
  • +
  • Checking for string containment is nice: if substring in string.
  • +
+

Where Ruby Rocks

+
    +
  • Consistency.
  • +
  • Blocks.
  • +
  • Excellent functional tools to deal with Enumerable.
  • +
  • Meta-programming.
  • +
  • Optional parentheses.
  • +
  • Modules and classes are also objects.
  • +
+

 

+]]>
+ http://blog.boochtek.com/2013/02/22/my-thoughts-on-python-vs-ruby/feed + 1 +
+ + Debugging Pattern – Grenade + http://blog.boochtek.com/2012/01/11/grenade-debugging-pattern + http://blog.boochtek.com/2012/01/11/grenade-debugging-pattern#respond + Thu, 12 Jan 2012 00:59:59 +0000 + + + + + http://blog.boochtek.com/?p=67 + Continue reading "Debugging Pattern – Grenade"]]> + I’ve been more interested in programming patterns lately, partly due to the Ruby community’s recent interest — especially the Ruby Rogue podcast’s love affair with “Smalltalk Best Practice Patterns”.

+

I consider most of the patterns in “Smalltalk Best Practice Patterns” to be patterns “in the small” — things that are typically employed on a single line or method. The Gang Of Four patterns are more medium sized, dealing with methods and classes. The PEAA book covers architectural-scale patterns. I suppose “The Pragmatic Programmer” and similar books could (should!) be considered to be very general patterns, mostly about the practice of programming.

+

One type of pattern that I have not seen discussed much is debugging patterns. I’m not exactly sure why that is; probably just our general concentration on designing the program code itself. There are definitely testing patterns. But I can’t think of anything that I’ve seen that covers debugging patterns. A quick search on the Internet doesn’t turn up too much.

+

Anyway, a co-worker (Helena) and I were debugging recently, and were having trouble figuring out where a certain method on a certain object was being called. We came up with a really cool solution. We immediately called it a grenade, because its entire purpose is to blow up (raise an exception) and display a backtrace to show us the caller of the method that we’re looking for.

+

Here’s our implementation in Ruby:

+

+module Grenade
+  def method_under_investigation(*)
+    raise "method_under_investigation called"
+  end
+end
+
+class Xyz
+  ...
+  def xyz
+    object_under_investigation.extend(Grenade)
+  end
+  ...
+end
+

I’m sure we could wrap that in some more semantic sugar, even going as far as making it look something like this:

+

+class Xyz
+  ...
+  def xyz
+    object_under_investigation.blow_up_when_method_called(:method_under_investigation)
+  end
+  ...
+end
+
+

I’m not sure that would really be worth it though, unless we were to add it to some sort of library.

+

So that’s our contribution to the (small) list of debugging patterns.

+]]>
+ http://blog.boochtek.com/2012/01/11/grenade-debugging-pattern/feed + 0 +
+
+
diff --git a/public/category/programming/programming-languages/shell.html b/public/category/programming/programming-languages/shell.html new file mode 100644 index 0000000..51fbf7e --- /dev/null +++ b/public/category/programming/programming-languages/shell.html @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + +Shell – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Burying the Lede

+ + + +
+

Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+
+ + +
+ +
+
+ +

Yak Shaving #1: Cursor Keys

+ + + +
+

I recently decided to start using Emacs again. I used it extensively from the early 1990s until the early 2000s. I pretty much stopped using it when I had a sysadmin job with no Emacs on the servers, and no ability to install it. With the rising popularity of tmux and tmate for remote pairing, and my dislike for vim’s modes, I decided to try going back to Emacs in the terminal.

+

One thing I really want in a text editor is good cursor key support. Shifted cursor keys
+should select text, and Control+left and right should move by words. (Apple HIG says to use Option+left and right to move by words; most apps on Mac OS X seem to support both.) Things have worked this way on almost every text editor on every OS I’ve used — Amiga, DOS, Windows, NeXT, Mac, Motif, Gnome, KDE. It’s a part of the CUA standard that’s been in common usage on everything since the mid-1980s.

+

Enabling cursor keys in Emacs was pretty easy. I’ve decided to use Prelude to make getting started with Emacs easy. Emacs comes with the cursor keys enabled, but Prelude disables them. Undoing Prelude’s change is pretty easy:

+
(setq prelude-guru nil)
+

Trying to make shifted cursor keys work is where the trouble began. They work in the GUI version of Emacs, but not from the terminal. It turns out that the Mac Terminal doesn’t distinguish between cursor keys and shifted cursor keys in its default configuration. So I had to figure out how to configure key bindings in Terminal.

+

That’s easy enough — they’re in the preferences. But what should I set them to? This took a lot of research. Terminal emulation and ANSI code sequences are obscure, complex, and inconsistent. I eventually found the info I needed. For starters, Shift+Right, Shift+Left, Shift+Home, and Shift+End are defined in the terminfo. The rest I was able to piece together from various sources around the Internet.

+

I’m also trying to script my Mac configuration. So instead of manually adding all the keybindings in the Terminal preferences pane, I decided to write a script. Mac OS X does a decent job of allowing you to change preferences from the command line. For example, to always show the tab bar:

+
defaults write -app Terminal ShowTabBar 1
+

Easy enough, except for a couple problems. First, I had to figure out the obscure syntax used in the preferences for key codes. I was able to piece these together with some more Internet research. But the really big problem is that the keyboard bindings are 2 levels deep within a “dictionary” (hash map). And the defaults command doesn’t handle that. There are some obscure utilities that handle nested preferences, but they don’t work well with the preferences caching in Mac OS X 10.9 — a problem I ran into while testing.

+

So now I’m writing a utility in Python that does what the defaults command does, but that will handle nested dictionaries.

+

There’s a term for this process of having to solve one issue before you can solve another, and the issues get several layers deep. It’s called yak shaving.

+

Here’s another good example:

+
Father from Malcolm in the middle having to fix one thing before fixing another, ad infinitum.
Yak shaving – home repairs
+

I’m sure this will be the first of many posts about me yak shaving.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/programming/programming-languages/shell/feed b/public/category/programming/programming-languages/shell/feed new file mode 100644 index 0000000..d05da75 --- /dev/null +++ b/public/category/programming/programming-languages/shell/feed @@ -0,0 +1,91 @@ + + + + Shell – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Burying the Lede + http://blog.boochtek.com/2014/03/11/readable-shell-scripts + http://blog.boochtek.com/2014/03/11/readable-shell-scripts#respond + Wed, 12 Mar 2014 04:28:22 +0000 + + + + + + http://blog.boochtek.com/?p=110 + Continue reading "Burying the Lede"]]> + Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+]]>
+ http://blog.boochtek.com/2014/03/11/readable-shell-scripts/feed + 0 +
+ + Yak Shaving #1: Cursor Keys + http://blog.boochtek.com/2014/03/03/yak-shaving-cursor-keys + http://blog.boochtek.com/2014/03/03/yak-shaving-cursor-keys#respond + Tue, 04 Mar 2014 04:02:59 +0000 + + + + + http://blog.boochtek.com/?p=129 + Continue reading "Yak Shaving #1: Cursor Keys"]]> + I recently decided to start using Emacs again. I used it extensively from the early 1990s until the early 2000s. I pretty much stopped using it when I had a sysadmin job with no Emacs on the servers, and no ability to install it. With the rising popularity of tmux and tmate for remote pairing, and my dislike for vim’s modes, I decided to try going back to Emacs in the terminal.

+

One thing I really want in a text editor is good cursor key support. Shifted cursor keys
+should select text, and Control+left and right should move by words. (Apple HIG says to use Option+left and right to move by words; most apps on Mac OS X seem to support both.) Things have worked this way on almost every text editor on every OS I’ve used — Amiga, DOS, Windows, NeXT, Mac, Motif, Gnome, KDE. It’s a part of the CUA standard that’s been in common usage on everything since the mid-1980s.

+

Enabling cursor keys in Emacs was pretty easy. I’ve decided to use Prelude to make getting started with Emacs easy. Emacs comes with the cursor keys enabled, but Prelude disables them. Undoing Prelude’s change is pretty easy:

+
(setq prelude-guru nil)
+

Trying to make shifted cursor keys work is where the trouble began. They work in the GUI version of Emacs, but not from the terminal. It turns out that the Mac Terminal doesn’t distinguish between cursor keys and shifted cursor keys in its default configuration. So I had to figure out how to configure key bindings in Terminal.

+

That’s easy enough — they’re in the preferences. But what should I set them to? This took a lot of research. Terminal emulation and ANSI code sequences are obscure, complex, and inconsistent. I eventually found the info I needed. For starters, Shift+Right, Shift+Left, Shift+Home, and Shift+End are defined in the terminfo. The rest I was able to piece together from various sources around the Internet.

+

I’m also trying to script my Mac configuration. So instead of manually adding all the keybindings in the Terminal preferences pane, I decided to write a script. Mac OS X does a decent job of allowing you to change preferences from the command line. For example, to always show the tab bar:

+
defaults write -app Terminal ShowTabBar 1
+

Easy enough, except for a couple problems. First, I had to figure out the obscure syntax used in the preferences for key codes. I was able to piece these together with some more Internet research. But the really big problem is that the keyboard bindings are 2 levels deep within a “dictionary” (hash map). And the defaults command doesn’t handle that. There are some obscure utilities that handle nested preferences, but they don’t work well with the preferences caching in Mac OS X 10.9 — a problem I ran into while testing.

+

So now I’m writing a utility in Python that does what the defaults command does, but that will handle nested dictionaries.

+

There’s a term for this process of having to solve one issue before you can solve another, and the issues get several layers deep. It’s called yak shaving.

+

Here’s another good example:

+
Father from Malcolm in the middle having to fix one thing before fixing another, ad infinitum.
Yak shaving – home repairs
+

I’m sure this will be the first of many posts about me yak shaving.

+]]>
+ http://blog.boochtek.com/2014/03/03/yak-shaving-cursor-keys/feed + 0 +
+
+
diff --git a/public/category/resolutions.html b/public/category/resolutions.html new file mode 100644 index 0000000..cb9523b --- /dev/null +++ b/public/category/resolutions.html @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + +Resolutions – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Resolutions for 2016

+ + + +
+

I’ve written up a list of career-related resolutions the past few years. I’ve been pretty successful meeting those goals, so I’m going to continue the tradition.

+

Coding

+

I want to increase the Open Source code I write this year. That’s basically everything I write that’s not directly related to work.

+

I took over maintenance of Virtus late last year, but haven’t done a good job at finding the time for that. That’s probably more responding to issues on GitHub and merging pull requests. But eventually, I’ll likely add some features and do some refactoring.

+

My goal is to work on 6 apps and libraries this year. That’s a pretty aggressive goal, but I want to focus on both learning new things and building some simple but useful things. Think MVP — focus on getting a lot of bang for not too much effort.

+

I want to work on a new Rails app, to familiarize myself with Rails 5. I want to get some experience with some more modern gems. I also want to see if I can use some new techniques with a Rails app, especially things that might help us get closer to a hexagonal architecture. I’m also hoping to work with some different web frameworks — maybe Lotus, Rodakase, Trailblazer, or Phoenix. Or maybe one of the Crystal web frameworks.

+

I also want to work on a couple Elm apps. I started the STL Elm group as an excuse to learn the language. I’d like to work on a Twitter client, that might eventually turn into a more general reading app. Beyond Twitter, it would keep track of things I want to read. The other Elm app I’d like to write is a game, based on the old Space Taxi game I loved to play on the Commodore 64.

+

I wrote a micro-ORM last year called Ruby Preserves. I’ve started converting that from working with raw SQL to sitting atop the awesome Sequel gem. The simplicity is still there so far, and basing it on Sequel has so far gone easier than I expected. But I haven’t figured out how to do relationships (JOINs) yet; that will be the real test.

+

Conferences

+

I’m pretty happy with my 2015 conference experience. I want to keep my conference level about the same. I don’t really want to commit to more talks than I gave last year. Conference presentations take a lot of time and energy to write and practice.

+

I’m planning to go to RailsConf, RubyConf, and Strange Loop. I’m hoping to give a talk at each of those, if I can. But I’ll probably attend all of them even if I’m not speaking. I’m also submitting a talk to Agile 2016. I’m unlikely to go to any other conferences, and even more unlikely to give more than 4 conference talks.

+

Writing

+

I really want to finish my Effective Agile book this year. I’m going to try to pull Amos in to pair with me on it occasionally, to move it forward. If I’m able to get it done, I’d also like to maybe write a book on Elm.

+

I actually want to do less blogging this year. Well, sort of. I want to put coding and book writing ahead of blogging. If that means less blogging, I’m okay with that. So my goal for blogging is more like every other week. And when I do write a blog article, I’d rather it be related to some code I’m working on, something that I could use in my book, or some more in-depth thoughts about programming language design.

+

Job Hunting

+

The contract I recently started at CenturyLink Cloud is open-ended, so I’ll likely be there for the entire year. I’m enjoying it so far; it’s a challenge, but I think I can make a significant impact. However, I’m really interested in moving to San Francisco in 2017. So I’m going to work on preparing for that in several ways.

+

I’m going to resurrect my LinkedIn account. I’ve neglected it for a few years now. I’ll make sure everything is up to date, and make some connections I’ve been putting off. I also want to reclaim my Stack Exchange and Hacker News accounts.

+

I’m going to pay a an expert to help me revamp my résumé. I think it’s pretty decent, but it could use some freshening up. I want it to stand out. I want to do a better job of explaining what I really do — which is to join a team to help them improve their processes and their technical skills.

+

I’ll also need to clean up all my web sites, so they look nice when people come to take a look at them. This includes my personal site, consulting business site, wiki, and blogs. I should also upgrade my server to the latest version of Debian, and use Ansible and some other tools to provision it using the principles of Infrastructure as Code.

+
+ + +
+ +
+
+ +

2015 Year in Review

+ + + +
+

It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+
+ + +
+ +
+
+ +

Resolutions

+ + + +
+

January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/resolutions/feed b/public/category/resolutions/feed new file mode 100644 index 0000000..ce290b3 --- /dev/null +++ b/public/category/resolutions/feed @@ -0,0 +1,139 @@ + + + + Resolutions – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Resolutions for 2016 + http://blog.boochtek.com/2016/01/19/resolutions-2016 + http://blog.boochtek.com/2016/01/19/resolutions-2016#respond + Wed, 20 Jan 2016 05:51:09 +0000 + + + + http://blog.boochtek.com/?p=290 + Continue reading "Resolutions for 2016"]]> + I’ve written up a list of career-related resolutions the past few years. I’ve been pretty successful meeting those goals, so I’m going to continue the tradition.

+

Coding

+

I want to increase the Open Source code I write this year. That’s basically everything I write that’s not directly related to work.

+

I took over maintenance of Virtus late last year, but haven’t done a good job at finding the time for that. That’s probably more responding to issues on GitHub and merging pull requests. But eventually, I’ll likely add some features and do some refactoring.

+

My goal is to work on 6 apps and libraries this year. That’s a pretty aggressive goal, but I want to focus on both learning new things and building some simple but useful things. Think MVP — focus on getting a lot of bang for not too much effort.

+

I want to work on a new Rails app, to familiarize myself with Rails 5. I want to get some experience with some more modern gems. I also want to see if I can use some new techniques with a Rails app, especially things that might help us get closer to a hexagonal architecture. I’m also hoping to work with some different web frameworks — maybe Lotus, Rodakase, Trailblazer, or Phoenix. Or maybe one of the Crystal web frameworks.

+

I also want to work on a couple Elm apps. I started the STL Elm group as an excuse to learn the language. I’d like to work on a Twitter client, that might eventually turn into a more general reading app. Beyond Twitter, it would keep track of things I want to read. The other Elm app I’d like to write is a game, based on the old Space Taxi game I loved to play on the Commodore 64.

+

I wrote a micro-ORM last year called Ruby Preserves. I’ve started converting that from working with raw SQL to sitting atop the awesome Sequel gem. The simplicity is still there so far, and basing it on Sequel has so far gone easier than I expected. But I haven’t figured out how to do relationships (JOINs) yet; that will be the real test.

+

Conferences

+

I’m pretty happy with my 2015 conference experience. I want to keep my conference level about the same. I don’t really want to commit to more talks than I gave last year. Conference presentations take a lot of time and energy to write and practice.

+

I’m planning to go to RailsConf, RubyConf, and Strange Loop. I’m hoping to give a talk at each of those, if I can. But I’ll probably attend all of them even if I’m not speaking. I’m also submitting a talk to Agile 2016. I’m unlikely to go to any other conferences, and even more unlikely to give more than 4 conference talks.

+

Writing

+

I really want to finish my Effective Agile book this year. I’m going to try to pull Amos in to pair with me on it occasionally, to move it forward. If I’m able to get it done, I’d also like to maybe write a book on Elm.

+

I actually want to do less blogging this year. Well, sort of. I want to put coding and book writing ahead of blogging. If that means less blogging, I’m okay with that. So my goal for blogging is more like every other week. And when I do write a blog article, I’d rather it be related to some code I’m working on, something that I could use in my book, or some more in-depth thoughts about programming language design.

+

Job Hunting

+

The contract I recently started at CenturyLink Cloud is open-ended, so I’ll likely be there for the entire year. I’m enjoying it so far; it’s a challenge, but I think I can make a significant impact. However, I’m really interested in moving to San Francisco in 2017. So I’m going to work on preparing for that in several ways.

+

I’m going to resurrect my LinkedIn account. I’ve neglected it for a few years now. I’ll make sure everything is up to date, and make some connections I’ve been putting off. I also want to reclaim my Stack Exchange and Hacker News accounts.

+

I’m going to pay a an expert to help me revamp my résumé. I think it’s pretty decent, but it could use some freshening up. I want it to stand out. I want to do a better job of explaining what I really do — which is to join a team to help them improve their processes and their technical skills.

+

I’ll also need to clean up all my web sites, so they look nice when people come to take a look at them. This includes my personal site, consulting business site, wiki, and blogs. I should also upgrade my server to the latest version of Debian, and use Ansible and some other tools to provision it using the principles of Infrastructure as Code.

+]]>
+ http://blog.boochtek.com/2016/01/19/resolutions-2016/feed + 0 +
+ + 2015 Year in Review + http://blog.boochtek.com/2015/12/28/year-in-review-2015 + http://blog.boochtek.com/2015/12/28/year-in-review-2015#respond + Tue, 29 Dec 2015 05:28:38 +0000 + + + + + + + + http://blog.boochtek.com/?p=287 + Continue reading "2015 Year in Review"]]> + It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+]]>
+ http://blog.boochtek.com/2015/12/28/year-in-review-2015/feed + 0 +
+ + Resolutions + http://blog.boochtek.com/2015/02/02/resolutions + http://blog.boochtek.com/2015/02/02/resolutions#respond + Tue, 03 Feb 2015 05:42:13 +0000 + + + + + + + + http://blog.boochtek.com/?p=198 + Continue reading "Resolutions"]]> + January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+]]>
+ http://blog.boochtek.com/2015/02/02/resolutions/feed + 0 +
+
+
diff --git a/public/category/security.html b/public/category/security.html new file mode 100644 index 0000000..a3a3dcb --- /dev/null +++ b/public/category/security.html @@ -0,0 +1,331 @@ + + + + + + + + + + + + + + + +Security – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Potential F5 Vulnerability

+ + + +
+

It all started with an email about a WebInspect report. It listed a buffer overflow, which we had marked as a false positive. I read the WebInspect report carefully, and found a note at the bottom that said you could test manually to confirm whether it was a false positive or not. Unfortunately, the manual test listed had a few problems. First, it jammed the lines together, without the proper line-breaks. Second, it assumed the site was using HTTP, not HTTPS, so used telnet. Third, it was testing against a page that didn’t exist, giving a 404. Keeping those in mind, I tried the manual test using the openssl s_client command:

+
openssl s_client -quiet -connect mysite.example.com:443
+POST /login HTTP/1.1
+Host: mysite.example.com
+Transfer-Encoding: chunked
+
+c00000000
+

The test terminated immediately. The report said that this meant that the server was vulnerable to a buffer overflow and arbitrary code execution.

+

At first, we thought this was caused by Apache or Tomcat, or possibly the application code. But the reported vulnerability was an Apache CVE from 2002 (CVE-2002-0392), fixed by vendors long ago. After a while, we realized that if we hit the servers directly, we did not get the indication of a vulnerability. If we hit the site through the F5 VIP, we saw the immediate termination of the connection. The issue is with handling of HTTP chunked encoding. Nginx had a similar issue in 2013 (CVE-2013-2028).

+

So we turned our attention to the F5 load balancers. We were able to confirm that other sites using F5 load balancers were exhibiting the same behavior. We also confirmed that WebInspect run against the servers directly did not show the issue (even as a false positive). We reported the issue to F5, and they are looking into it.

+

I’m disclosing this publicly now for a few reasons. First, I’m not a security researcher, and almost nobody follows my blog — especially people looking for security issues that could be exploited. Second, I’ve not developed a payload, so I have no idea whether this is exploitable. But at this point, it’s merely a potential vulnerability. I’m not sure I’ll even spend the time to research and create a payload to prove the vulnerability. If I do, I’ll be more careful with the disclosure.

+

 

+
+ + +
+ +
+
+ +

Resolutions

+ + + +
+

January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+
+ + +
+ +
+
+ +

Empathy

+ + + +
+

I facilitated our team retrospective this morning. I felt like we made a little forward progress, but not as much as I would have liked. But it really brought one thing to the forefront of my thoughts today — empathy gained through communication.

+

We have a pretty large team by Agile standards — we had 20 people in our retro: 16 developers, 3 QA folks, and 1 manager. Out of those, only about 5 or 6 speak up regularly. I recently sent out a survey to the team, trying to get feedback on how we could improve our retros. A couple of the questions tried to get a feel for why people aren’t speaking up more. Only about half the people responded, and the answers didn’t really answer my question as well as I had hoped.

+

So on Amos‘s suggestion, we did the Safety Check exercise. We got a good set of answers to why people don’t feel safe. About half of the answers were about the fear of looking stupid in front of other people. About half of those mentioned the manager — they’re worried they might get in trouble for what they say. We talked some about fear and how it’s more often than not misplaced. And that the worst consequences are usually not as bad as you might think. But then we got to the crux of the problem — there’s not enough trust amongst the team, and especially between the team members and the manager.

+

About half of our team is new (within the past 6 months) — including the manager. While the developers have made some good progress building trust amongst each other, we haven’t had as much time with the manager to build trust between him and the rest of the team. So the lack of trust isn’t at all surprising.

+

Honestly, I already knew we had trust issues, and wanted to address them, but needed a way to lead the team to that same realization. With this exercise driving out the issue, we were then able to have a conversation about trust. The conversation was pretty good. We got more voices to contribute than probably any other topic we’d previously covered. (I was disappointed that the manager kept quiet though. I later found that he was trying to mitigate people’s fears by keeping quiet, but I urged him to contribute more in the future.)

+

But one point really stood out in my mind — a point of view that I hadn’t previously thought much about. Lauren, one of our QA analysts, pointed out that most human communication is non-verbal. We give tons of cues via body language, facial expressions, eye contact, tone of voice, even posture. I don’t recall if Lauren said it explicitly, but she pointed out that these cues build empathy between the speakers. She encouraged us to use more voice chat and video chat, as opposed to IRC text chat, because it would create more empathy between the people communicating, which would lead to more trust.

+

I spent most of the rest of the day talking to people on the phone or via Google Hangouts voice. And every single time, I consciously noticed that I was gaining empathy for the person I was speaking with. I assume (and hope) that that’s working both ways. I suppose that it’s always been happening, but I never really noticed it.

+

I’ve heard a lot of talk about empathy among Agile practitioners lately. It’s been mentioned on the Ruby Rogues podcast, and Angela Harms has been preaching it for years. I already understood how important it is. But until today, I didn’t really feel it happening.

+

So if you’re looking to build trust with someone, spend some time talking with them. Preferably in person, but if that’s not possible, seriously consider video or voice modes of communication, instead of sending an email or an instant message.

+

 

+
+ + +
+ +
+
+ +

What I Want in a Blog Engine

+ + + +
+

I’m considering moving away from WordPress, for a couple reasons:

+
    +
  • Security. There have been several vulnerabilities over the past few years, and I’ve never really had a high level of confidence that it’s secure. In addition, I now find the whole PHP model — every web app running as a single user, instead of leveraging UNIX permissions — to be broken.
  • +
  • Speed. I started to realize a couple years ago that a blog engine should generate static pages. The static pages should be updated whenever new content is added. There’s really no reason to re-generate the page every time someone wants to read it. At the very least, Server-Side Includes (SSI) or Edge-Side Includes (ESI) should be used.
  • +
+

Now that I’m starting to actually blog consistently, it makes sense to change. But before I move to something different, I want to be sure that I find all the features that I need. This post is my thought process about what I want.

+

Requirements

+
    +
  • Self hosted. I like to run my own servers. Partly so I can keep full control, and partly because I enjoy doing some system admin stuff (as long as it doesn’t take too much time).
  • +
  • Generation of static pages. Almost everything should be a static page by the time a user requests a page. Obviously, submitting comments would hit a dynamic page, and comment approval would likely be done through a dynamic part of the site. But publishing a blog post or comments should generate static files to be served by Apache or nginx.
  • +
  • Categories (or maybe tags). I want readers to be able to find other things that I’ve written that might interest them. A list of categories in a sidebar would help them find what interests them.
  • +
  • Site map. Especially an XML sitemap, so Google can index better. I also like the idea of readers being able to see everything in one place.
  • +
  • Archives. This is another way of letting readers see everything I’ve written, organized by publication date.
  • +
  • RSS and/or Atom. I want readers to be able to subscribe to my blog.
  • +
  • Media. I don’t think I necessarily need a media manager, just a simple way to add pictures to blog posts.
  • +
  • Comments. I want to allow comments, but I don’t want to use anything like Disqus. I want the comments to be in my own database, so they won’t disappear. I also dislike requiring JavaScript just to see comments. And I’ve worked at places where Disqus wouldn’t work, due to web proxy servers. Comments should be moderated by the site admin, editor, or author. Readers should not have to log in to post a comment, but it would be nice to allow users to log into Twitter or some other social network to provide their info.
  • +
  • Pingbacks and Tweetbacks. I’d like to be able to see who’s referring to my articles.
  • +
+

Nice to Have

+
    +
  • Free. I almost consider this to be a requirement, but I’d be willing to entertain a 1-time payment for something that meets my needs really well. I need to try to counteract my DIY-at-all-non-monetary-costs mentality.
  • +
  • Open Source. I think Open Source is likely to provide a more useful product, especially in this space. An Open Source option is more likely to improve over time.
  • +
  • Not PHP. I’ve got several problems with PHP. First, the whole PHP model of running in the same context as Apache (or nginx) breaks UNIX conventions. A security issue in a PHP program exposes more than just the program itself — it exposes Apache and every other PHP program. Under the UNIX model, each program runs in a different user context, isolating bugs to that program only. PHP is also a “hacky” language. PHP programmers tend to not be as rigorous (in many ways), leading to more vulnerabilities. Finally, the PHP language itself has had several misfeatures that have lead to security issues — much more than Ruby or Java.
  • +
  • Not MySQL. I’d like to be able to remove MySQL from my servers. PostgreSQL would be much preferred. Storing everything in git would be fine too — maybe even preferred. I’d even be fine with MongoDB or some other simple NoSQL database.
  • +
  • Admin console. I can’t imagine approving comments without this, but I’m not closed to that possibility.
  • +
  • Edit history. I want to be able to update posts, but see what the previous versions looked like. (Not sure if I care whether the public can or cannot see the various revisions.)
  • +
  • WYSIWYG. I’d be willing to settle for Markdown. In fact, I’d prefer (extended) Markdown as the canonical form behind the WYSIWYG.
  • +
  • Code highlighting. A lot of my blog posts (will) include code in various languages. I’d like the blogging software to be able to handle that well. Syntax highlighting would be great. Any other related features would also be nice.
  • +
  • Through-the-web editing. I’d like to be able to edit a page from any device from anywhere that has Internet access.
  • +
  • Reader-submitted edits. This is probably going to be the hardest thing to find, but I’d like a really easy way for readers to let me know of typos and such by simply submitting a fix for them. The author or editor of the post would have to approve the edits, obviously.
  • +
  • Import from WordPress. I don’t have a ton of content in WordPress, or else this would probably be a more firm requirement.
  • +
  • Community. Community helps software thrive, adding improvements and ensuring security.
  • +
  • Plugins. It would be nice for the blog engine to be extensible.
  • +
  • Unpublished drafts and previews. I’d really like to be able to see by blog posts before I release them to the world.
  • +
  • CMS abilities. I’d really like the ability to edit non-blog web pages the same way as blog entries.
  • +
  • Themes. It would be nice to have decently looking themes, so I could have other people help me make the site look nice.
  • +
  • Posting my tweets in a sidebar area. I’d be OK with this being done in client-side JavaScript. I’d also be OK with periodic polling of my Twitter feed, and updating the static content when appropriate.
  • +
+

Candidates

+

I’ve been playing with Jekyll lately, using it to generate a simple static site that can be updated easily. It’s pretty nice, and pretty easy to use. Something based on Jekyll would work well. The trickiest part will be figuring out how to manage comments. Or maybe I’ll just have to learn to live more minimally.

+

Octopress is the obvious candidate. It fits most of my needs and wants. Most likely I’ll write my own commenting engine and add it to my Octopress template.

+

The only other candidate that I’ve found is Ghost. It’s written in JavaScript and runs on Node. I don’t find Node to be that compelling, but the templates look great, and it’s designed for coding blogs.

+

I’ll probably take a quick look at Droplets, too. It’s written in PHP, but seems to fit my other requirements.

+

Unfortunately, that seems to be all there is in this space. Maybe I need to investigate more. Or maybe Octopress just has the static blog generation marked cornered. Either way, I’ll probably change my blogging software in the next month or two. Wish me luck!

+

 

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/security/feed b/public/category/security/feed new file mode 100644 index 0000000..9c0e13a --- /dev/null +++ b/public/category/security/feed @@ -0,0 +1,169 @@ + + + + Security – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Potential F5 Vulnerability + http://blog.boochtek.com/2015/08/31/potential-f5-vulnerability + http://blog.boochtek.com/2015/08/31/potential-f5-vulnerability#respond + Tue, 01 Sep 2015 03:52:53 +0000 + + + + http://blog.boochtek.com/?p=249 + Continue reading "Potential F5 Vulnerability"]]> + It all started with an email about a WebInspect report. It listed a buffer overflow, which we had marked as a false positive. I read the WebInspect report carefully, and found a note at the bottom that said you could test manually to confirm whether it was a false positive or not. Unfortunately, the manual test listed had a few problems. First, it jammed the lines together, without the proper line-breaks. Second, it assumed the site was using HTTP, not HTTPS, so used telnet. Third, it was testing against a page that didn’t exist, giving a 404. Keeping those in mind, I tried the manual test using the openssl s_client command:

+
openssl s_client -quiet -connect mysite.example.com:443
+POST /login HTTP/1.1
+Host: mysite.example.com
+Transfer-Encoding: chunked
+
+c00000000
+

The test terminated immediately. The report said that this meant that the server was vulnerable to a buffer overflow and arbitrary code execution.

+

At first, we thought this was caused by Apache or Tomcat, or possibly the application code. But the reported vulnerability was an Apache CVE from 2002 (CVE-2002-0392), fixed by vendors long ago. After a while, we realized that if we hit the servers directly, we did not get the indication of a vulnerability. If we hit the site through the F5 VIP, we saw the immediate termination of the connection. The issue is with handling of HTTP chunked encoding. Nginx had a similar issue in 2013 (CVE-2013-2028).

+

So we turned our attention to the F5 load balancers. We were able to confirm that other sites using F5 load balancers were exhibiting the same behavior. We also confirmed that WebInspect run against the servers directly did not show the issue (even as a false positive). We reported the issue to F5, and they are looking into it.

+

I’m disclosing this publicly now for a few reasons. First, I’m not a security researcher, and almost nobody follows my blog — especially people looking for security issues that could be exploited. Second, I’ve not developed a payload, so I have no idea whether this is exploitable. But at this point, it’s merely a potential vulnerability. I’m not sure I’ll even spend the time to research and create a payload to prove the vulnerability. If I do, I’ll be more careful with the disclosure.

+

 

+]]>
+ http://blog.boochtek.com/2015/08/31/potential-f5-vulnerability/feed + 0 +
+ + Resolutions + http://blog.boochtek.com/2015/02/02/resolutions + http://blog.boochtek.com/2015/02/02/resolutions#respond + Tue, 03 Feb 2015 05:42:13 +0000 + + + + + + + + http://blog.boochtek.com/?p=198 + Continue reading "Resolutions"]]> + January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+]]>
+ http://blog.boochtek.com/2015/02/02/resolutions/feed + 0 +
+ + Empathy + http://blog.boochtek.com/2014/02/07/empathy + http://blog.boochtek.com/2014/02/07/empathy#respond + Sat, 08 Feb 2014 03:23:48 +0000 + + + + + http://blog.boochtek.com/?p=116 + Continue reading "Empathy"]]> + I facilitated our team retrospective this morning. I felt like we made a little forward progress, but not as much as I would have liked. But it really brought one thing to the forefront of my thoughts today — empathy gained through communication.

+

We have a pretty large team by Agile standards — we had 20 people in our retro: 16 developers, 3 QA folks, and 1 manager. Out of those, only about 5 or 6 speak up regularly. I recently sent out a survey to the team, trying to get feedback on how we could improve our retros. A couple of the questions tried to get a feel for why people aren’t speaking up more. Only about half the people responded, and the answers didn’t really answer my question as well as I had hoped.

+

So on Amos‘s suggestion, we did the Safety Check exercise. We got a good set of answers to why people don’t feel safe. About half of the answers were about the fear of looking stupid in front of other people. About half of those mentioned the manager — they’re worried they might get in trouble for what they say. We talked some about fear and how it’s more often than not misplaced. And that the worst consequences are usually not as bad as you might think. But then we got to the crux of the problem — there’s not enough trust amongst the team, and especially between the team members and the manager.

+

About half of our team is new (within the past 6 months) — including the manager. While the developers have made some good progress building trust amongst each other, we haven’t had as much time with the manager to build trust between him and the rest of the team. So the lack of trust isn’t at all surprising.

+

Honestly, I already knew we had trust issues, and wanted to address them, but needed a way to lead the team to that same realization. With this exercise driving out the issue, we were then able to have a conversation about trust. The conversation was pretty good. We got more voices to contribute than probably any other topic we’d previously covered. (I was disappointed that the manager kept quiet though. I later found that he was trying to mitigate people’s fears by keeping quiet, but I urged him to contribute more in the future.)

+

But one point really stood out in my mind — a point of view that I hadn’t previously thought much about. Lauren, one of our QA analysts, pointed out that most human communication is non-verbal. We give tons of cues via body language, facial expressions, eye contact, tone of voice, even posture. I don’t recall if Lauren said it explicitly, but she pointed out that these cues build empathy between the speakers. She encouraged us to use more voice chat and video chat, as opposed to IRC text chat, because it would create more empathy between the people communicating, which would lead to more trust.

+

I spent most of the rest of the day talking to people on the phone or via Google Hangouts voice. And every single time, I consciously noticed that I was gaining empathy for the person I was speaking with. I assume (and hope) that that’s working both ways. I suppose that it’s always been happening, but I never really noticed it.

+

I’ve heard a lot of talk about empathy among Agile practitioners lately. It’s been mentioned on the Ruby Rogues podcast, and Angela Harms has been preaching it for years. I already understood how important it is. But until today, I didn’t really feel it happening.

+

So if you’re looking to build trust with someone, spend some time talking with them. Preferably in person, but if that’s not possible, seriously consider video or voice modes of communication, instead of sending an email or an instant message.

+

 

+]]>
+ http://blog.boochtek.com/2014/02/07/empathy/feed + 0 +
+ + What I Want in a Blog Engine + http://blog.boochtek.com/2014/02/02/blogging-software + http://blog.boochtek.com/2014/02/02/blogging-software#respond + Mon, 03 Feb 2014 04:50:43 +0000 + + + + + + http://blog.boochtek.com/?p=100 + Continue reading "What I Want in a Blog Engine"]]> + I’m considering moving away from WordPress, for a couple reasons:

+
    +
  • Security. There have been several vulnerabilities over the past few years, and I’ve never really had a high level of confidence that it’s secure. In addition, I now find the whole PHP model — every web app running as a single user, instead of leveraging UNIX permissions — to be broken.
  • +
  • Speed. I started to realize a couple years ago that a blog engine should generate static pages. The static pages should be updated whenever new content is added. There’s really no reason to re-generate the page every time someone wants to read it. At the very least, Server-Side Includes (SSI) or Edge-Side Includes (ESI) should be used.
  • +
+

Now that I’m starting to actually blog consistently, it makes sense to change. But before I move to something different, I want to be sure that I find all the features that I need. This post is my thought process about what I want.

+

Requirements

+
    +
  • Self hosted. I like to run my own servers. Partly so I can keep full control, and partly because I enjoy doing some system admin stuff (as long as it doesn’t take too much time).
  • +
  • Generation of static pages. Almost everything should be a static page by the time a user requests a page. Obviously, submitting comments would hit a dynamic page, and comment approval would likely be done through a dynamic part of the site. But publishing a blog post or comments should generate static files to be served by Apache or nginx.
  • +
  • Categories (or maybe tags). I want readers to be able to find other things that I’ve written that might interest them. A list of categories in a sidebar would help them find what interests them.
  • +
  • Site map. Especially an XML sitemap, so Google can index better. I also like the idea of readers being able to see everything in one place.
  • +
  • Archives. This is another way of letting readers see everything I’ve written, organized by publication date.
  • +
  • RSS and/or Atom. I want readers to be able to subscribe to my blog.
  • +
  • Media. I don’t think I necessarily need a media manager, just a simple way to add pictures to blog posts.
  • +
  • Comments. I want to allow comments, but I don’t want to use anything like Disqus. I want the comments to be in my own database, so they won’t disappear. I also dislike requiring JavaScript just to see comments. And I’ve worked at places where Disqus wouldn’t work, due to web proxy servers. Comments should be moderated by the site admin, editor, or author. Readers should not have to log in to post a comment, but it would be nice to allow users to log into Twitter or some other social network to provide their info.
  • +
  • Pingbacks and Tweetbacks. I’d like to be able to see who’s referring to my articles.
  • +
+

Nice to Have

+
    +
  • Free. I almost consider this to be a requirement, but I’d be willing to entertain a 1-time payment for something that meets my needs really well. I need to try to counteract my DIY-at-all-non-monetary-costs mentality.
  • +
  • Open Source. I think Open Source is likely to provide a more useful product, especially in this space. An Open Source option is more likely to improve over time.
  • +
  • Not PHP. I’ve got several problems with PHP. First, the whole PHP model of running in the same context as Apache (or nginx) breaks UNIX conventions. A security issue in a PHP program exposes more than just the program itself — it exposes Apache and every other PHP program. Under the UNIX model, each program runs in a different user context, isolating bugs to that program only. PHP is also a “hacky” language. PHP programmers tend to not be as rigorous (in many ways), leading to more vulnerabilities. Finally, the PHP language itself has had several misfeatures that have lead to security issues — much more than Ruby or Java.
  • +
  • Not MySQL. I’d like to be able to remove MySQL from my servers. PostgreSQL would be much preferred. Storing everything in git would be fine too — maybe even preferred. I’d even be fine with MongoDB or some other simple NoSQL database.
  • +
  • Admin console. I can’t imagine approving comments without this, but I’m not closed to that possibility.
  • +
  • Edit history. I want to be able to update posts, but see what the previous versions looked like. (Not sure if I care whether the public can or cannot see the various revisions.)
  • +
  • WYSIWYG. I’d be willing to settle for Markdown. In fact, I’d prefer (extended) Markdown as the canonical form behind the WYSIWYG.
  • +
  • Code highlighting. A lot of my blog posts (will) include code in various languages. I’d like the blogging software to be able to handle that well. Syntax highlighting would be great. Any other related features would also be nice.
  • +
  • Through-the-web editing. I’d like to be able to edit a page from any device from anywhere that has Internet access.
  • +
  • Reader-submitted edits. This is probably going to be the hardest thing to find, but I’d like a really easy way for readers to let me know of typos and such by simply submitting a fix for them. The author or editor of the post would have to approve the edits, obviously.
  • +
  • Import from WordPress. I don’t have a ton of content in WordPress, or else this would probably be a more firm requirement.
  • +
  • Community. Community helps software thrive, adding improvements and ensuring security.
  • +
  • Plugins. It would be nice for the blog engine to be extensible.
  • +
  • Unpublished drafts and previews. I’d really like to be able to see by blog posts before I release them to the world.
  • +
  • CMS abilities. I’d really like the ability to edit non-blog web pages the same way as blog entries.
  • +
  • Themes. It would be nice to have decently looking themes, so I could have other people help me make the site look nice.
  • +
  • Posting my tweets in a sidebar area. I’d be OK with this being done in client-side JavaScript. I’d also be OK with periodic polling of my Twitter feed, and updating the static content when appropriate.
  • +
+

Candidates

+

I’ve been playing with Jekyll lately, using it to generate a simple static site that can be updated easily. It’s pretty nice, and pretty easy to use. Something based on Jekyll would work well. The trickiest part will be figuring out how to manage comments. Or maybe I’ll just have to learn to live more minimally.

+

Octopress is the obvious candidate. It fits most of my needs and wants. Most likely I’ll write my own commenting engine and add it to my Octopress template.

+

The only other candidate that I’ve found is Ghost. It’s written in JavaScript and runs on Node. I don’t find Node to be that compelling, but the templates look great, and it’s designed for coding blogs.

+

I’ll probably take a quick look at Droplets, too. It’s written in PHP, but seems to fit my other requirements.

+

Unfortunately, that seems to be all there is in this space. Maybe I need to investigate more. Or maybe Octopress just has the static blog generation marked cornered. Either way, I’ll probably change my blogging software in the next month or two. Wish me luck!

+

 

+]]>
+ http://blog.boochtek.com/2014/02/02/blogging-software/feed + 0 +
+
+
diff --git a/public/category/sysadmin.html b/public/category/sysadmin.html new file mode 100644 index 0000000..ec24e6d --- /dev/null +++ b/public/category/sysadmin.html @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + +System Admin – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Burying the Lede

+ + + +
+

Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+
+ + +
+ +
+
+ +

Yak Shaving #1: Cursor Keys

+ + + +
+

I recently decided to start using Emacs again. I used it extensively from the early 1990s until the early 2000s. I pretty much stopped using it when I had a sysadmin job with no Emacs on the servers, and no ability to install it. With the rising popularity of tmux and tmate for remote pairing, and my dislike for vim’s modes, I decided to try going back to Emacs in the terminal.

+

One thing I really want in a text editor is good cursor key support. Shifted cursor keys
+should select text, and Control+left and right should move by words. (Apple HIG says to use Option+left and right to move by words; most apps on Mac OS X seem to support both.) Things have worked this way on almost every text editor on every OS I’ve used — Amiga, DOS, Windows, NeXT, Mac, Motif, Gnome, KDE. It’s a part of the CUA standard that’s been in common usage on everything since the mid-1980s.

+

Enabling cursor keys in Emacs was pretty easy. I’ve decided to use Prelude to make getting started with Emacs easy. Emacs comes with the cursor keys enabled, but Prelude disables them. Undoing Prelude’s change is pretty easy:

+
(setq prelude-guru nil)
+

Trying to make shifted cursor keys work is where the trouble began. They work in the GUI version of Emacs, but not from the terminal. It turns out that the Mac Terminal doesn’t distinguish between cursor keys and shifted cursor keys in its default configuration. So I had to figure out how to configure key bindings in Terminal.

+

That’s easy enough — they’re in the preferences. But what should I set them to? This took a lot of research. Terminal emulation and ANSI code sequences are obscure, complex, and inconsistent. I eventually found the info I needed. For starters, Shift+Right, Shift+Left, Shift+Home, and Shift+End are defined in the terminfo. The rest I was able to piece together from various sources around the Internet.

+

I’m also trying to script my Mac configuration. So instead of manually adding all the keybindings in the Terminal preferences pane, I decided to write a script. Mac OS X does a decent job of allowing you to change preferences from the command line. For example, to always show the tab bar:

+
defaults write -app Terminal ShowTabBar 1
+

Easy enough, except for a couple problems. First, I had to figure out the obscure syntax used in the preferences for key codes. I was able to piece these together with some more Internet research. But the really big problem is that the keyboard bindings are 2 levels deep within a “dictionary” (hash map). And the defaults command doesn’t handle that. There are some obscure utilities that handle nested preferences, but they don’t work well with the preferences caching in Mac OS X 10.9 — a problem I ran into while testing.

+

So now I’m writing a utility in Python that does what the defaults command does, but that will handle nested dictionaries.

+

There’s a term for this process of having to solve one issue before you can solve another, and the issues get several layers deep. It’s called yak shaving.

+

Here’s another good example:

+
Father from Malcolm in the middle having to fix one thing before fixing another, ad infinitum.
Yak shaving – home repairs
+

I’m sure this will be the first of many posts about me yak shaving.

+
+ + +
+ +
+
+ +

What I Want in a Blog Engine

+ + + +
+

I’m considering moving away from WordPress, for a couple reasons:

+
    +
  • Security. There have been several vulnerabilities over the past few years, and I’ve never really had a high level of confidence that it’s secure. In addition, I now find the whole PHP model — every web app running as a single user, instead of leveraging UNIX permissions — to be broken.
  • +
  • Speed. I started to realize a couple years ago that a blog engine should generate static pages. The static pages should be updated whenever new content is added. There’s really no reason to re-generate the page every time someone wants to read it. At the very least, Server-Side Includes (SSI) or Edge-Side Includes (ESI) should be used.
  • +
+

Now that I’m starting to actually blog consistently, it makes sense to change. But before I move to something different, I want to be sure that I find all the features that I need. This post is my thought process about what I want.

+

Requirements

+
    +
  • Self hosted. I like to run my own servers. Partly so I can keep full control, and partly because I enjoy doing some system admin stuff (as long as it doesn’t take too much time).
  • +
  • Generation of static pages. Almost everything should be a static page by the time a user requests a page. Obviously, submitting comments would hit a dynamic page, and comment approval would likely be done through a dynamic part of the site. But publishing a blog post or comments should generate static files to be served by Apache or nginx.
  • +
  • Categories (or maybe tags). I want readers to be able to find other things that I’ve written that might interest them. A list of categories in a sidebar would help them find what interests them.
  • +
  • Site map. Especially an XML sitemap, so Google can index better. I also like the idea of readers being able to see everything in one place.
  • +
  • Archives. This is another way of letting readers see everything I’ve written, organized by publication date.
  • +
  • RSS and/or Atom. I want readers to be able to subscribe to my blog.
  • +
  • Media. I don’t think I necessarily need a media manager, just a simple way to add pictures to blog posts.
  • +
  • Comments. I want to allow comments, but I don’t want to use anything like Disqus. I want the comments to be in my own database, so they won’t disappear. I also dislike requiring JavaScript just to see comments. And I’ve worked at places where Disqus wouldn’t work, due to web proxy servers. Comments should be moderated by the site admin, editor, or author. Readers should not have to log in to post a comment, but it would be nice to allow users to log into Twitter or some other social network to provide their info.
  • +
  • Pingbacks and Tweetbacks. I’d like to be able to see who’s referring to my articles.
  • +
+

Nice to Have

+
    +
  • Free. I almost consider this to be a requirement, but I’d be willing to entertain a 1-time payment for something that meets my needs really well. I need to try to counteract my DIY-at-all-non-monetary-costs mentality.
  • +
  • Open Source. I think Open Source is likely to provide a more useful product, especially in this space. An Open Source option is more likely to improve over time.
  • +
  • Not PHP. I’ve got several problems with PHP. First, the whole PHP model of running in the same context as Apache (or nginx) breaks UNIX conventions. A security issue in a PHP program exposes more than just the program itself — it exposes Apache and every other PHP program. Under the UNIX model, each program runs in a different user context, isolating bugs to that program only. PHP is also a “hacky” language. PHP programmers tend to not be as rigorous (in many ways), leading to more vulnerabilities. Finally, the PHP language itself has had several misfeatures that have lead to security issues — much more than Ruby or Java.
  • +
  • Not MySQL. I’d like to be able to remove MySQL from my servers. PostgreSQL would be much preferred. Storing everything in git would be fine too — maybe even preferred. I’d even be fine with MongoDB or some other simple NoSQL database.
  • +
  • Admin console. I can’t imagine approving comments without this, but I’m not closed to that possibility.
  • +
  • Edit history. I want to be able to update posts, but see what the previous versions looked like. (Not sure if I care whether the public can or cannot see the various revisions.)
  • +
  • WYSIWYG. I’d be willing to settle for Markdown. In fact, I’d prefer (extended) Markdown as the canonical form behind the WYSIWYG.
  • +
  • Code highlighting. A lot of my blog posts (will) include code in various languages. I’d like the blogging software to be able to handle that well. Syntax highlighting would be great. Any other related features would also be nice.
  • +
  • Through-the-web editing. I’d like to be able to edit a page from any device from anywhere that has Internet access.
  • +
  • Reader-submitted edits. This is probably going to be the hardest thing to find, but I’d like a really easy way for readers to let me know of typos and such by simply submitting a fix for them. The author or editor of the post would have to approve the edits, obviously.
  • +
  • Import from WordPress. I don’t have a ton of content in WordPress, or else this would probably be a more firm requirement.
  • +
  • Community. Community helps software thrive, adding improvements and ensuring security.
  • +
  • Plugins. It would be nice for the blog engine to be extensible.
  • +
  • Unpublished drafts and previews. I’d really like to be able to see by blog posts before I release them to the world.
  • +
  • CMS abilities. I’d really like the ability to edit non-blog web pages the same way as blog entries.
  • +
  • Themes. It would be nice to have decently looking themes, so I could have other people help me make the site look nice.
  • +
  • Posting my tweets in a sidebar area. I’d be OK with this being done in client-side JavaScript. I’d also be OK with periodic polling of my Twitter feed, and updating the static content when appropriate.
  • +
+

Candidates

+

I’ve been playing with Jekyll lately, using it to generate a simple static site that can be updated easily. It’s pretty nice, and pretty easy to use. Something based on Jekyll would work well. The trickiest part will be figuring out how to manage comments. Or maybe I’ll just have to learn to live more minimally.

+

Octopress is the obvious candidate. It fits most of my needs and wants. Most likely I’ll write my own commenting engine and add it to my Octopress template.

+

The only other candidate that I’ve found is Ghost. It’s written in JavaScript and runs on Node. I don’t find Node to be that compelling, but the templates look great, and it’s designed for coding blogs.

+

I’ll probably take a quick look at Droplets, too. It’s written in PHP, but seems to fit my other requirements.

+

Unfortunately, that seems to be all there is in this space. Maybe I need to investigate more. Or maybe Octopress just has the static blog generation marked cornered. Either way, I’ll probably change my blogging software in the next month or two. Wish me luck!

+

 

+
+ + +
+ +
+
+ +

Bulk Rename in Bash

+ + + +
+

Here’s a relatively simple way to rename a bunch of files from the command line. It uses sed within a command substitution to compute the new names from the old names.

+

In this example, we’re renaming files that start with “ABC” to start with “XYZ” instead:

+
  for i in ABC*; do mv $i $(echo $i | sed -e s/^ABC/XYZ/); done
+

You’ll have to use shell globbing (wildcards) in the first part, to determine which files will be the source of the renaming, and regular expressions in the second part to translate the old names into the new names.

+

It’s a good idea to use echo in place of mv before running this, to see what the results will be, so you don’t make a mistake.

+

Depending on the situation, you may need to adapt this for your particular needs. For example, if you have too many files to rename, you would need to use xargs. Or sed might not be up for the task of transforming the names that you want. Or you might need to use find to find files within a hierarchy. As with any UNIX idiom, there are hundreds of variations on the theme.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/sysadmin/feed b/public/category/sysadmin/feed new file mode 100644 index 0000000..56a1c93 --- /dev/null +++ b/public/category/sysadmin/feed @@ -0,0 +1,173 @@ + + + + System Admin – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Burying the Lede + http://blog.boochtek.com/2014/03/11/readable-shell-scripts + http://blog.boochtek.com/2014/03/11/readable-shell-scripts#respond + Wed, 12 Mar 2014 04:28:22 +0000 + + + + + + http://blog.boochtek.com/?p=110 + Continue reading "Burying the Lede"]]> + Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+]]>
+ http://blog.boochtek.com/2014/03/11/readable-shell-scripts/feed + 0 +
+ + Yak Shaving #1: Cursor Keys + http://blog.boochtek.com/2014/03/03/yak-shaving-cursor-keys + http://blog.boochtek.com/2014/03/03/yak-shaving-cursor-keys#respond + Tue, 04 Mar 2014 04:02:59 +0000 + + + + + http://blog.boochtek.com/?p=129 + Continue reading "Yak Shaving #1: Cursor Keys"]]> + I recently decided to start using Emacs again. I used it extensively from the early 1990s until the early 2000s. I pretty much stopped using it when I had a sysadmin job with no Emacs on the servers, and no ability to install it. With the rising popularity of tmux and tmate for remote pairing, and my dislike for vim’s modes, I decided to try going back to Emacs in the terminal.

+

One thing I really want in a text editor is good cursor key support. Shifted cursor keys
+should select text, and Control+left and right should move by words. (Apple HIG says to use Option+left and right to move by words; most apps on Mac OS X seem to support both.) Things have worked this way on almost every text editor on every OS I’ve used — Amiga, DOS, Windows, NeXT, Mac, Motif, Gnome, KDE. It’s a part of the CUA standard that’s been in common usage on everything since the mid-1980s.

+

Enabling cursor keys in Emacs was pretty easy. I’ve decided to use Prelude to make getting started with Emacs easy. Emacs comes with the cursor keys enabled, but Prelude disables them. Undoing Prelude’s change is pretty easy:

+
(setq prelude-guru nil)
+

Trying to make shifted cursor keys work is where the trouble began. They work in the GUI version of Emacs, but not from the terminal. It turns out that the Mac Terminal doesn’t distinguish between cursor keys and shifted cursor keys in its default configuration. So I had to figure out how to configure key bindings in Terminal.

+

That’s easy enough — they’re in the preferences. But what should I set them to? This took a lot of research. Terminal emulation and ANSI code sequences are obscure, complex, and inconsistent. I eventually found the info I needed. For starters, Shift+Right, Shift+Left, Shift+Home, and Shift+End are defined in the terminfo. The rest I was able to piece together from various sources around the Internet.

+

I’m also trying to script my Mac configuration. So instead of manually adding all the keybindings in the Terminal preferences pane, I decided to write a script. Mac OS X does a decent job of allowing you to change preferences from the command line. For example, to always show the tab bar:

+
defaults write -app Terminal ShowTabBar 1
+

Easy enough, except for a couple problems. First, I had to figure out the obscure syntax used in the preferences for key codes. I was able to piece these together with some more Internet research. But the really big problem is that the keyboard bindings are 2 levels deep within a “dictionary” (hash map). And the defaults command doesn’t handle that. There are some obscure utilities that handle nested preferences, but they don’t work well with the preferences caching in Mac OS X 10.9 — a problem I ran into while testing.

+

So now I’m writing a utility in Python that does what the defaults command does, but that will handle nested dictionaries.

+

There’s a term for this process of having to solve one issue before you can solve another, and the issues get several layers deep. It’s called yak shaving.

+

Here’s another good example:

+
Father from Malcolm in the middle having to fix one thing before fixing another, ad infinitum.
Yak shaving – home repairs
+

I’m sure this will be the first of many posts about me yak shaving.

+]]>
+ http://blog.boochtek.com/2014/03/03/yak-shaving-cursor-keys/feed + 0 +
+ + What I Want in a Blog Engine + http://blog.boochtek.com/2014/02/02/blogging-software + http://blog.boochtek.com/2014/02/02/blogging-software#respond + Mon, 03 Feb 2014 04:50:43 +0000 + + + + + + http://blog.boochtek.com/?p=100 + Continue reading "What I Want in a Blog Engine"]]> + I’m considering moving away from WordPress, for a couple reasons:

+
    +
  • Security. There have been several vulnerabilities over the past few years, and I’ve never really had a high level of confidence that it’s secure. In addition, I now find the whole PHP model — every web app running as a single user, instead of leveraging UNIX permissions — to be broken.
  • +
  • Speed. I started to realize a couple years ago that a blog engine should generate static pages. The static pages should be updated whenever new content is added. There’s really no reason to re-generate the page every time someone wants to read it. At the very least, Server-Side Includes (SSI) or Edge-Side Includes (ESI) should be used.
  • +
+

Now that I’m starting to actually blog consistently, it makes sense to change. But before I move to something different, I want to be sure that I find all the features that I need. This post is my thought process about what I want.

+

Requirements

+
    +
  • Self hosted. I like to run my own servers. Partly so I can keep full control, and partly because I enjoy doing some system admin stuff (as long as it doesn’t take too much time).
  • +
  • Generation of static pages. Almost everything should be a static page by the time a user requests a page. Obviously, submitting comments would hit a dynamic page, and comment approval would likely be done through a dynamic part of the site. But publishing a blog post or comments should generate static files to be served by Apache or nginx.
  • +
  • Categories (or maybe tags). I want readers to be able to find other things that I’ve written that might interest them. A list of categories in a sidebar would help them find what interests them.
  • +
  • Site map. Especially an XML sitemap, so Google can index better. I also like the idea of readers being able to see everything in one place.
  • +
  • Archives. This is another way of letting readers see everything I’ve written, organized by publication date.
  • +
  • RSS and/or Atom. I want readers to be able to subscribe to my blog.
  • +
  • Media. I don’t think I necessarily need a media manager, just a simple way to add pictures to blog posts.
  • +
  • Comments. I want to allow comments, but I don’t want to use anything like Disqus. I want the comments to be in my own database, so they won’t disappear. I also dislike requiring JavaScript just to see comments. And I’ve worked at places where Disqus wouldn’t work, due to web proxy servers. Comments should be moderated by the site admin, editor, or author. Readers should not have to log in to post a comment, but it would be nice to allow users to log into Twitter or some other social network to provide their info.
  • +
  • Pingbacks and Tweetbacks. I’d like to be able to see who’s referring to my articles.
  • +
+

Nice to Have

+
    +
  • Free. I almost consider this to be a requirement, but I’d be willing to entertain a 1-time payment for something that meets my needs really well. I need to try to counteract my DIY-at-all-non-monetary-costs mentality.
  • +
  • Open Source. I think Open Source is likely to provide a more useful product, especially in this space. An Open Source option is more likely to improve over time.
  • +
  • Not PHP. I’ve got several problems with PHP. First, the whole PHP model of running in the same context as Apache (or nginx) breaks UNIX conventions. A security issue in a PHP program exposes more than just the program itself — it exposes Apache and every other PHP program. Under the UNIX model, each program runs in a different user context, isolating bugs to that program only. PHP is also a “hacky” language. PHP programmers tend to not be as rigorous (in many ways), leading to more vulnerabilities. Finally, the PHP language itself has had several misfeatures that have lead to security issues — much more than Ruby or Java.
  • +
  • Not MySQL. I’d like to be able to remove MySQL from my servers. PostgreSQL would be much preferred. Storing everything in git would be fine too — maybe even preferred. I’d even be fine with MongoDB or some other simple NoSQL database.
  • +
  • Admin console. I can’t imagine approving comments without this, but I’m not closed to that possibility.
  • +
  • Edit history. I want to be able to update posts, but see what the previous versions looked like. (Not sure if I care whether the public can or cannot see the various revisions.)
  • +
  • WYSIWYG. I’d be willing to settle for Markdown. In fact, I’d prefer (extended) Markdown as the canonical form behind the WYSIWYG.
  • +
  • Code highlighting. A lot of my blog posts (will) include code in various languages. I’d like the blogging software to be able to handle that well. Syntax highlighting would be great. Any other related features would also be nice.
  • +
  • Through-the-web editing. I’d like to be able to edit a page from any device from anywhere that has Internet access.
  • +
  • Reader-submitted edits. This is probably going to be the hardest thing to find, but I’d like a really easy way for readers to let me know of typos and such by simply submitting a fix for them. The author or editor of the post would have to approve the edits, obviously.
  • +
  • Import from WordPress. I don’t have a ton of content in WordPress, or else this would probably be a more firm requirement.
  • +
  • Community. Community helps software thrive, adding improvements and ensuring security.
  • +
  • Plugins. It would be nice for the blog engine to be extensible.
  • +
  • Unpublished drafts and previews. I’d really like to be able to see by blog posts before I release them to the world.
  • +
  • CMS abilities. I’d really like the ability to edit non-blog web pages the same way as blog entries.
  • +
  • Themes. It would be nice to have decently looking themes, so I could have other people help me make the site look nice.
  • +
  • Posting my tweets in a sidebar area. I’d be OK with this being done in client-side JavaScript. I’d also be OK with periodic polling of my Twitter feed, and updating the static content when appropriate.
  • +
+

Candidates

+

I’ve been playing with Jekyll lately, using it to generate a simple static site that can be updated easily. It’s pretty nice, and pretty easy to use. Something based on Jekyll would work well. The trickiest part will be figuring out how to manage comments. Or maybe I’ll just have to learn to live more minimally.

+

Octopress is the obvious candidate. It fits most of my needs and wants. Most likely I’ll write my own commenting engine and add it to my Octopress template.

+

The only other candidate that I’ve found is Ghost. It’s written in JavaScript and runs on Node. I don’t find Node to be that compelling, but the templates look great, and it’s designed for coding blogs.

+

I’ll probably take a quick look at Droplets, too. It’s written in PHP, but seems to fit my other requirements.

+

Unfortunately, that seems to be all there is in this space. Maybe I need to investigate more. Or maybe Octopress just has the static blog generation marked cornered. Either way, I’ll probably change my blogging software in the next month or two. Wish me luck!

+

 

+]]>
+ http://blog.boochtek.com/2014/02/02/blogging-software/feed + 0 +
+ + Bulk Rename in Bash + http://blog.boochtek.com/2011/08/26/bulk-rename-in-bash + http://blog.boochtek.com/2011/08/26/bulk-rename-in-bash#respond + Sat, 27 Aug 2011 03:53:58 +0000 + + + + + http://blog.boochtek.com/?p=46 + Continue reading "Bulk Rename in Bash"]]> + Here’s a relatively simple way to rename a bunch of files from the command line. It uses sed within a command substitution to compute the new names from the old names.

+

In this example, we’re renaming files that start with “ABC” to start with “XYZ” instead:

+
  for i in ABC*; do mv $i $(echo $i | sed -e s/^ABC/XYZ/); done
+

You’ll have to use shell globbing (wildcards) in the first part, to determine which files will be the source of the renaming, and regular expressions in the second part to translate the old names into the new names.

+

It’s a good idea to use echo in place of mv before running this, to see what the results will be, so you don’t make a mistake.

+

Depending on the situation, you may need to adapt this for your particular needs. For example, if you have too many files to rename, you would need to use xargs. Or sed might not be up for the task of transforming the names that you want. Or you might need to use find to find files within a hierarchy. As with any UNIX idiom, there are hundreds of variations on the theme.

+]]>
+ http://blog.boochtek.com/2011/08/26/bulk-rename-in-bash/feed + 0 +
+
+
diff --git a/public/category/uncategorized.html b/public/category/uncategorized.html new file mode 100644 index 0000000..e5c659e --- /dev/null +++ b/public/category/uncategorized.html @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + +Uncategorized – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

My First Open Space

+ + + +
+

I recently attended an Open Space hosted at work. I’d never been to an Open Space, and didn’t know what to expect. We’d been told that this was a workshop to help Engineering Managers (my role), Product Owners, and Product Analysts find better ways of working together. But due to the way an Open Space works, it evolved into something completely different — and better.

+

We’d brought in Diana Larsen to facilitate the Open Space. Diana is a stalwart of the Agile community, focusing on how people and teams interact. She literally (co-)wrote the book on Agile retrospectives. Diana was also kind enough to be our guest at an Agile LINC meetup later in the evening.

+

The morning started off with all the participants sitting in chairs arranged in a circle. Diana rang a chime, a nice soothing tone that literally set the tone for the day. (Most people didn’t seem to like it, but I thought it had its purpose.) It also acted as a call to gather in the meeting space. She then walked around the inside of the circle as she explained what was going to happen.

+

The idea behind Open Spaces came with the realization that at a conference, the “hallway track” (ad hoc discussions in the hallways) was often more valuable than the scheduled talks. So they figured out a way to capture that experience. There are only a few rules:

+
    +
  • Whoever shows up is the right group
  • +
  • Whatever happens is the only thing that could have
  • +
  • Be prepared to be surprised
  • +
  • Whenever it starts is the right time
  • +
  • When it’s over, it’s over
  • +
  • The Law of 2 Feet: If you’re not learning or contributing, go somewhere else
  • +
+

Once Diana set the scene and explained the rules, people came up and presented ideas for topics. If you proposed a topic, you chose a time and location on the topic board. At that time, you’d facilitate a discussion on that topic. The format was really conducive to discussions. There was no preparation, so discussions were from the heart — lots of people felt that they could contribute.

+

Being new to the company (only 2 months), I got a lot out of the workshop, beyond the content itself. I got to get to know several more people. I’d even say I made a few friends. Having open discussions, we found that many of the teams were having the same issues. This made it easy for me to talk to other people.

+

All-in-all, I found the Open Space to be extremely conducive to discussions aimed at identifying issues, brainstorming solutions, and planning action items. I felt like we made great strides in addressing some of the biggest problems our organization is currently facing.

+
+ + +
+ +
+
+ +

Resolutions for 2016

+ + + +
+

I’ve written up a list of career-related resolutions the past few years. I’ve been pretty successful meeting those goals, so I’m going to continue the tradition.

+

Coding

+

I want to increase the Open Source code I write this year. That’s basically everything I write that’s not directly related to work.

+

I took over maintenance of Virtus late last year, but haven’t done a good job at finding the time for that. That’s probably more responding to issues on GitHub and merging pull requests. But eventually, I’ll likely add some features and do some refactoring.

+

My goal is to work on 6 apps and libraries this year. That’s a pretty aggressive goal, but I want to focus on both learning new things and building some simple but useful things. Think MVP — focus on getting a lot of bang for not too much effort.

+

I want to work on a new Rails app, to familiarize myself with Rails 5. I want to get some experience with some more modern gems. I also want to see if I can use some new techniques with a Rails app, especially things that might help us get closer to a hexagonal architecture. I’m also hoping to work with some different web frameworks — maybe Lotus, Rodakase, Trailblazer, or Phoenix. Or maybe one of the Crystal web frameworks.

+

I also want to work on a couple Elm apps. I started the STL Elm group as an excuse to learn the language. I’d like to work on a Twitter client, that might eventually turn into a more general reading app. Beyond Twitter, it would keep track of things I want to read. The other Elm app I’d like to write is a game, based on the old Space Taxi game I loved to play on the Commodore 64.

+

I wrote a micro-ORM last year called Ruby Preserves. I’ve started converting that from working with raw SQL to sitting atop the awesome Sequel gem. The simplicity is still there so far, and basing it on Sequel has so far gone easier than I expected. But I haven’t figured out how to do relationships (JOINs) yet; that will be the real test.

+

Conferences

+

I’m pretty happy with my 2015 conference experience. I want to keep my conference level about the same. I don’t really want to commit to more talks than I gave last year. Conference presentations take a lot of time and energy to write and practice.

+

I’m planning to go to RailsConf, RubyConf, and Strange Loop. I’m hoping to give a talk at each of those, if I can. But I’ll probably attend all of them even if I’m not speaking. I’m also submitting a talk to Agile 2016. I’m unlikely to go to any other conferences, and even more unlikely to give more than 4 conference talks.

+

Writing

+

I really want to finish my Effective Agile book this year. I’m going to try to pull Amos in to pair with me on it occasionally, to move it forward. If I’m able to get it done, I’d also like to maybe write a book on Elm.

+

I actually want to do less blogging this year. Well, sort of. I want to put coding and book writing ahead of blogging. If that means less blogging, I’m okay with that. So my goal for blogging is more like every other week. And when I do write a blog article, I’d rather it be related to some code I’m working on, something that I could use in my book, or some more in-depth thoughts about programming language design.

+

Job Hunting

+

The contract I recently started at CenturyLink Cloud is open-ended, so I’ll likely be there for the entire year. I’m enjoying it so far; it’s a challenge, but I think I can make a significant impact. However, I’m really interested in moving to San Francisco in 2017. So I’m going to work on preparing for that in several ways.

+

I’m going to resurrect my LinkedIn account. I’ve neglected it for a few years now. I’ll make sure everything is up to date, and make some connections I’ve been putting off. I also want to reclaim my Stack Exchange and Hacker News accounts.

+

I’m going to pay a an expert to help me revamp my résumé. I think it’s pretty decent, but it could use some freshening up. I want it to stand out. I want to do a better job of explaining what I really do — which is to join a team to help them improve their processes and their technical skills.

+

I’ll also need to clean up all my web sites, so they look nice when people come to take a look at them. This includes my personal site, consulting business site, wiki, and blogs. I should also upgrade my server to the latest version of Debian, and use Ansible and some other tools to provision it using the principles of Infrastructure as Code.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/uncategorized/feed b/public/category/uncategorized/feed new file mode 100644 index 0000000..46f62d9 --- /dev/null +++ b/public/category/uncategorized/feed @@ -0,0 +1,21 @@ + + + + Uncategorized – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + diff --git a/public/category/webdev.html b/public/category/webdev.html new file mode 100644 index 0000000..35c96ab --- /dev/null +++ b/public/category/webdev.html @@ -0,0 +1,765 @@ + + + + + + + + + + + + + + + +Web Development – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Website Checklist

+ + + +
+

While creating a web page isn’t too difficult, there are a lot of moving parts involved in creating an effective website. We’ve written up this checklist as a guide for anyone who has decided that they need a website.

+

This list is for a basic static website. A web application will require significantly more effort. We’re working on a separate checklist for web apps.

+

Overview

+
    +
  • Determine your goals
  • +
  • Develop a business plan
  • +
  • Register a domain name
  • +
  • Set up DNS hosting
  • +
  • Set up web hosting
  • +
  • Design and develop the site
  • +
  • Deploy and test the site
  • +
  • Market your site
  • +
  • Set up additional services
  • +
  • Maintenance
  • +
+

Goals

+
    +
  • What do you want to achieve from having the site?
  • +
  • What “call to action” do you want visitors to take?
  • +
  • How will you measure success?
  • +
  • What will be the focus of the site? +
      +
    • Info (“brochure”) for an existing business
    • +
    • Blogging
    • +
    • Sales
    • +
    • Community
    • +
    • Web app
    • +
    • Mobile app
    • +
    +
  • +
+

Business Plan

+
    +
  • Who is your target audience?
  • +
  • Marketing +
      +
    • How will you get them to come to your site?
    • +
    • How will you get them to buy something?
    • +
    +
  • +
  • Who is your competition? +
      +
    • How do you compare to them?
    • +
    • How do you differentiate from them?
    • +
    • What is your niche?
    • +
    • What makes your site better than theirs?
    • +
    +
  • +
  • How will you make money? +
      +
    • Bringing people into brick and mortar business
    • +
    • Advertising
    • +
    • Periodic billing/subscriptions
    • +
    • Selling goods/services
    • +
    • Get bought out
    • +
    +
  • +
  • Pricing +
      +
    • Aim high — it’s easier to lower prices than raise them
    • +
    • Tiered pricing often makes sense; most people pick the middle tier
    • +
    +
  • +
  • What will it take to have a positive ROI?
  • +
+

Domain Name

+

You’ll probably want your own domain name.

+
    +
  • Think of a name +
      +
    • Stick with a .com name if possible; don’t use .biz
    • +
    • Some other top-level domains come into fashion on occasion +
        +
      • .io is pretty popular right now
      • +
      +
    • +
    +
  • +
  • Check availability of the name
  • +
  • Keep checking names, until you find a good name that is available
  • +
  • Register the name with a respectable registrar +
      +
    • DNS Registrars run from $10-$35/yr
    • +
    • DO NOT allow your web host provider to own/control your name
    • +
    • You may want to grab the .net, .org, and other versions of the same name
    • +
    • Multiple-year registration is cheaper, but it’s easier to forget how to renew it
    • +
    +
  • +
  • Your registrar will likely point your domain to an “under construction” page initially
  • +
  • DO NOT LOSE YOUR NAME! +
      +
    • Spammers and pornographers will take it over if your registration lapses
    • +
    • Make sure your contact info (especially email address) is up-to-date
    • +
    +
  • +
  • Beware scams (usually by US mail) trying to get you to renew with a different registrar
  • +
  • Note that the email address you register with will get spammed +
      +
    • Some registrars provide some protection for a fee
    • +
    +
  • +
+

DNS Hosting

+

You’ll need to have someone take care of the servers that tell the world what server addresses your domain name corresponds to.

+
    +
  • Find a DNS hosting provider +
      +
    • We like DNSimple; they also do domain registration
    • +
    +
  • +
  • Provide the name servers to your DNS registrar
  • +
+

Web Hosting

+

You’ll need servers to host your web site on. There are a lot of options available, from virtual hosts (a Linux server where you control everything) to application-specific services.

+
    +
  • Talk to your developer or designer first! +
      +
    • Web host can significantly restrain the development environment
    • +
    +
  • +
  • Cost +
      +
    • $10 to $500 / month is typical
    • +
    +
  • +
  • Bandwidth +
      +
    • Number of users × how often they use it × average “page” size
    • +
    • What happens if you go over?
    • +
    +
  • +
  • Up-time +
      +
    • What kind of down-time can the site sustain?
    • +
    • Higher guaranteed uptime costs more
    • +
    • What if the specified uptime is not met?
    • +
    +
  • +
  • Development environment +
      +
    • What programming languages are installed?
    • +
    • What databases are installed?
    • +
    • What libraries are installed?
    • +
    • What if other libraries are required?
    • +
    +
  • +
  • Shared/dedicated/virtual hosting +
      +
    • Shared means others are using the same machine, with security implications
    • +
    • Dedicated is expensive, but you “own” the whole machine
    • +
    • Virtual is somewhere in between +
        +
      • You have a “slice” of a machine dedicated to your site
      • +
      +
    • +
    +
  • +
  • How responsive is the host to problems and requests?
  • +
  • Backups +
      +
    • What do they back up?
    • +
    • How often do they back up?
    • +
    • How can files be restored?
    • +
    +
  • +
+

Design and Development

+

Designing and developing the site can vary from picking an existing template and adding content, to developing a full web application.

+
    +
  • Cost ($30 – $300 / hr)
  • +
  • Project management +
      +
    • Story/task management
    • +
    +
  • +
  • Revision control +
      +
    • Ensures changes can be rolled back quickly
    • +
    +
  • +
  • Functionality +
      +
    • What does the site need to do?
    • +
    +
  • +
  • Usability +
      +
    • How easy is it to use each page?
    • +
    • Is it easy to navigate the site to find what you’re looking for?
    • +
    • Check for broken links
    • +
    • Check for ADA/508 compliance
    • +
    • Spell checking and grammar checking
    • +
    +
  • +
+

Deploying and Testing

+
    +
  • Can updates be deployed quickly? +
      +
    • Deploy early and often, so it’s not such a big deal, and it becomes routine
    • +
    +
  • +
  • Consider a staging and/or beta site +
      +
    • Test everything thoroughly on staging site before deploying to production
    • +
    • Staging site should (mostly) use the same code as production
    • +
    • Staging/test site should not process credit cards, etc.
    • +
    +
  • +
  • Automated testing +
      +
    • Prevents regressions
    • +
    +
  • +
  • Exploratory testing +
      +
    • See how things work
    • +
    • See if you can break things
    • +
    +
  • +
  • Security testing +
      +
    • Penetration testing
    • +
    +
  • +
  • Performance +
      +
    • Load testing
    • +
    +
  • +
  • Beta testers
  • +
+

Marketing

+
    +
  • Search engine “optimization” (SEO) +
      +
    • Good design, good URLs, and well-written HTML should cover most of this
    • +
    • Submit site to search engines
    • +
    • Use robots.txt and site maps
    • +
    +
  • +
  • Directories
  • +
  • PR sites
  • +
  • Targeted groups
  • +
  • DO NOT send out spam
  • +
+

Additional Services

+

What other services do you need, besides just the web site?

+
    +
  • Email
  • +
  • Blog
  • +
  • Wiki
  • +
  • File storage
  • +
  • Forums
  • +
  • Authentication
  • +
  • Customer interaction +
      +
    • CRM
    • +
    • Feedback
    • +
    • Bug tracking
    • +
    • Customer Q&A (StackExchange)
    • +
    • Follow-up emails to customers to offer assistance
    • +
    +
  • +
+

Maintenance

+

Over the lifetime of the site, you’ll likely pay more in maintenance costs than the upfront costs.

+
    +
  • Responding to user emails
  • +
  • Requests for info
  • +
  • Feedback about the site
  • +
  • Password resets?
  • +
  • Tracking bug reports and feature requests
  • +
  • Site improvements
  • +
  • Additional content
  • +
  • Moderation of user content +
      +
    • Spam removal
    • +
    +
  • +
  • Log analysis +
      +
    • Google Analytics
    • +
    +
  • +
  • Assessing advertising effectiveness
  • +
  • Analysis of revenues/profitability
  • +
  • Upgrades +
      +
    • New/improved functionality
    • +
    • Bug fixes
    • +
    • Upgraded infrastructure
    • +
    +
  • +
  • Down-time +
      +
    • Web host
    • +
    • Upgrades
    • +
    • Accidents
    • +
    • Bugs
    • +
    +
  • +
  • Backups +
      +
    • Testing restoring from backups
    • +
    +
  • +
  • Payments for services +
      +
    • Domain name registration – DO NOT LOSE YOUR NAME!
    • +
    • Web hosting
    • +
    • Marketing/advertising
    • +
    +
  • +
+

 

+
+ + +
+ +
+
+ +

Not Quite Callbacks

+ + + +
+

I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+
+ + +
+ +
+
+ +

Architectural Thoughts

+ + + +
+

I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+
+ + +
+ +
+
+ +

Hexagonal Rails Controllers

+ + + +
+

I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+
+ + +
+ +
+
+ +

TDD Is Alive And Well

+ + + +
+

I went to RailsConf this year, and the very first talk was a keynote by David Heinemeier Hansson (DHH), the creator of Ruby on Rails. The TL;DR of his talk was “TDD rarely has value”. He followed up with a blog post the next day, titled “TDD is dead. Long live testing.“, and 2 more posts. I think this line of thought is terribly misguided, and causing more harm than good. This article is my response.

+

First, I would like to address the good points of the talk. He said that programming is pseudoscience, and that people want to tell us that there’s a secret to being a better programmer. But what it really takes is working hard — reading a lot of code, writing a lot of code, and rewriting a lot of code. He’s right. And I also agree with him that you should forget about patterns for a while when learning to code. Beginners try to throw patterns at a problem instead of letting the patterns emerge where they’re supposed to.

+

I don’t completely agree that programming is a pseudoscience. In some ways it is, but I think it’s more of a craft. It’s a craft, because there’s a lot of science involved, but there’s also an art to doing it well. And like any craft, you’re always working to get better. So to respond to DHH’s stance that “software is more like poetry than physics”, I think it falls
+somewhere in between.

+

With regard to the software engineering practices we use, there really isn’t much science available, mostly because it’s a soft science. That is, it’s really hard to isolate a single variable when comparing code between projects. And nobody has the time or money to write the same code so many times that the differences would be statistically significant.

+

So we don’t have much science on TDD. But we do have some. Here’s a collection of several: StudiesOfTestDrivenDevelopment. And here’s one that explicitly looks are the difference between test-first and test-last: Does Test-Driven Development Really Improve Software Design Quality? What do these tell us? They tell us that TDD costs us about 10-30% in short-term productivity; reduces bugs by 30-90%, and decreases code complexity by about 30%. As Code Complete tells us (in section 20.5, with studies to back it up), improving quality reduces development costs. So, like most Agile practices, this is a case where spending a bit more time in the short term leads to time savings in the long term.

+

The more important lesson in the talk was that you have to do what works best for you and your situation. If TDD doesn’t give better results, then either find out how to make it give better results, or stop using it. As we often say in the Agile world, Agile doesn’t mean that you can stop using your brain. While I think TDD is appropriate in most situations, there are cases where it’s not worth the additional up-front cost. If the most important thing for your project is time-to-market, then not testing might be the right decision for you.

+

To me, TDD provides a bunch of benefits. First and foremost, TDD is a design discipline. It ensures that I think about how my code will be used before I think about how to implement it. This is very powerful in ensuring that the code is well-written from the perspective of other code using it.

+

Tested code provides confidence to be able to make changes without breaking things. If we write tests after the code, we’re less likely to write them. Tests written after the code also tend to test the implementation instead of the desired functionality. What we really want is tests written as a specification. With tests as a specification, we can come back later and understand why code was written. Without tests, or with poor tests, we can’t understand why the code is there; if we want to rewrite it, we don’t have the confidence that we’re not missing something. Writing tests first also ensures that we only write the code that is needed to implement the required functionality.

+

I’m not sure why DHH hasn’t “gotten” TDD. I’m not sure if it’s because he’s  a better coder than average, or if he just thinks in a different way than most of us. I think it’s partly because he doesn’t understand TDD, which he admitted might be the case. And I think he’s conflating TDD and unit testing.

+

DHH is influential in the developer community, especially those newer to Ruby and Rails. People listen to what he has to say. I was happy to see almost every other speaker made fun of DHH’s ideas, and most of the crowd knew better. But there will be a lot of others who will hear DHH, respect his opinions, and not give TDD the try that it deserves. And that’s sad, because it will lead to an overall reduction in code quality in the world.

+

Here are some other people’s thoughts on the matter:

+ +

 

+
+ + +
+ +
+
+ +

Includable ActiveRecord

+ + + +
+

I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+
+ + +
+ +
+
+ +

Testing Rails Validators

+ + + +
+

It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/webdev/feed b/public/category/webdev/feed new file mode 100644 index 0000000..864485c --- /dev/null +++ b/public/category/webdev/feed @@ -0,0 +1,609 @@ + + + + Web Development – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Website Checklist + http://blog.boochtek.com/2015/09/08/website-checklist-2 + http://blog.boochtek.com/2015/09/08/website-checklist-2#respond + Wed, 09 Sep 2015 03:38:02 +0000 + + + + http://blog.boochtek.com/?p=254 + Continue reading "Website Checklist"]]> + While creating a web page isn’t too difficult, there are a lot of moving parts involved in creating an effective website. We’ve written up this checklist as a guide for anyone who has decided that they need a website.

+

This list is for a basic static website. A web application will require significantly more effort. We’re working on a separate checklist for web apps.

+

Overview

+
    +
  • Determine your goals
  • +
  • Develop a business plan
  • +
  • Register a domain name
  • +
  • Set up DNS hosting
  • +
  • Set up web hosting
  • +
  • Design and develop the site
  • +
  • Deploy and test the site
  • +
  • Market your site
  • +
  • Set up additional services
  • +
  • Maintenance
  • +
+

Goals

+
    +
  • What do you want to achieve from having the site?
  • +
  • What “call to action” do you want visitors to take?
  • +
  • How will you measure success?
  • +
  • What will be the focus of the site? +
      +
    • Info (“brochure”) for an existing business
    • +
    • Blogging
    • +
    • Sales
    • +
    • Community
    • +
    • Web app
    • +
    • Mobile app
    • +
    +
  • +
+

Business Plan

+
    +
  • Who is your target audience?
  • +
  • Marketing +
      +
    • How will you get them to come to your site?
    • +
    • How will you get them to buy something?
    • +
    +
  • +
  • Who is your competition? +
      +
    • How do you compare to them?
    • +
    • How do you differentiate from them?
    • +
    • What is your niche?
    • +
    • What makes your site better than theirs?
    • +
    +
  • +
  • How will you make money? +
      +
    • Bringing people into brick and mortar business
    • +
    • Advertising
    • +
    • Periodic billing/subscriptions
    • +
    • Selling goods/services
    • +
    • Get bought out
    • +
    +
  • +
  • Pricing +
      +
    • Aim high — it’s easier to lower prices than raise them
    • +
    • Tiered pricing often makes sense; most people pick the middle tier
    • +
    +
  • +
  • What will it take to have a positive ROI?
  • +
+

Domain Name

+

You’ll probably want your own domain name.

+
    +
  • Think of a name +
      +
    • Stick with a .com name if possible; don’t use .biz
    • +
    • Some other top-level domains come into fashion on occasion +
        +
      • .io is pretty popular right now
      • +
      +
    • +
    +
  • +
  • Check availability of the name
  • +
  • Keep checking names, until you find a good name that is available
  • +
  • Register the name with a respectable registrar +
      +
    • DNS Registrars run from $10-$35/yr
    • +
    • DO NOT allow your web host provider to own/control your name
    • +
    • You may want to grab the .net, .org, and other versions of the same name
    • +
    • Multiple-year registration is cheaper, but it’s easier to forget how to renew it
    • +
    +
  • +
  • Your registrar will likely point your domain to an “under construction” page initially
  • +
  • DO NOT LOSE YOUR NAME! +
      +
    • Spammers and pornographers will take it over if your registration lapses
    • +
    • Make sure your contact info (especially email address) is up-to-date
    • +
    +
  • +
  • Beware scams (usually by US mail) trying to get you to renew with a different registrar
  • +
  • Note that the email address you register with will get spammed +
      +
    • Some registrars provide some protection for a fee
    • +
    +
  • +
+

DNS Hosting

+

You’ll need to have someone take care of the servers that tell the world what server addresses your domain name corresponds to.

+
    +
  • Find a DNS hosting provider +
      +
    • We like DNSimple; they also do domain registration
    • +
    +
  • +
  • Provide the name servers to your DNS registrar
  • +
+

Web Hosting

+

You’ll need servers to host your web site on. There are a lot of options available, from virtual hosts (a Linux server where you control everything) to application-specific services.

+
    +
  • Talk to your developer or designer first! +
      +
    • Web host can significantly restrain the development environment
    • +
    +
  • +
  • Cost +
      +
    • $10 to $500 / month is typical
    • +
    +
  • +
  • Bandwidth +
      +
    • Number of users × how often they use it × average “page” size
    • +
    • What happens if you go over?
    • +
    +
  • +
  • Up-time +
      +
    • What kind of down-time can the site sustain?
    • +
    • Higher guaranteed uptime costs more
    • +
    • What if the specified uptime is not met?
    • +
    +
  • +
  • Development environment +
      +
    • What programming languages are installed?
    • +
    • What databases are installed?
    • +
    • What libraries are installed?
    • +
    • What if other libraries are required?
    • +
    +
  • +
  • Shared/dedicated/virtual hosting +
      +
    • Shared means others are using the same machine, with security implications
    • +
    • Dedicated is expensive, but you “own” the whole machine
    • +
    • Virtual is somewhere in between +
        +
      • You have a “slice” of a machine dedicated to your site
      • +
      +
    • +
    +
  • +
  • How responsive is the host to problems and requests?
  • +
  • Backups +
      +
    • What do they back up?
    • +
    • How often do they back up?
    • +
    • How can files be restored?
    • +
    +
  • +
+

Design and Development

+

Designing and developing the site can vary from picking an existing template and adding content, to developing a full web application.

+
    +
  • Cost ($30 – $300 / hr)
  • +
  • Project management +
      +
    • Story/task management
    • +
    +
  • +
  • Revision control +
      +
    • Ensures changes can be rolled back quickly
    • +
    +
  • +
  • Functionality +
      +
    • What does the site need to do?
    • +
    +
  • +
  • Usability +
      +
    • How easy is it to use each page?
    • +
    • Is it easy to navigate the site to find what you’re looking for?
    • +
    • Check for broken links
    • +
    • Check for ADA/508 compliance
    • +
    • Spell checking and grammar checking
    • +
    +
  • +
+

Deploying and Testing

+
    +
  • Can updates be deployed quickly? +
      +
    • Deploy early and often, so it’s not such a big deal, and it becomes routine
    • +
    +
  • +
  • Consider a staging and/or beta site +
      +
    • Test everything thoroughly on staging site before deploying to production
    • +
    • Staging site should (mostly) use the same code as production
    • +
    • Staging/test site should not process credit cards, etc.
    • +
    +
  • +
  • Automated testing +
      +
    • Prevents regressions
    • +
    +
  • +
  • Exploratory testing +
      +
    • See how things work
    • +
    • See if you can break things
    • +
    +
  • +
  • Security testing +
      +
    • Penetration testing
    • +
    +
  • +
  • Performance +
      +
    • Load testing
    • +
    +
  • +
  • Beta testers
  • +
+

Marketing

+
    +
  • Search engine “optimization” (SEO) +
      +
    • Good design, good URLs, and well-written HTML should cover most of this
    • +
    • Submit site to search engines
    • +
    • Use robots.txt and site maps
    • +
    +
  • +
  • Directories
  • +
  • PR sites
  • +
  • Targeted groups
  • +
  • DO NOT send out spam
  • +
+

Additional Services

+

What other services do you need, besides just the web site?

+
    +
  • Email
  • +
  • Blog
  • +
  • Wiki
  • +
  • File storage
  • +
  • Forums
  • +
  • Authentication
  • +
  • Customer interaction +
      +
    • CRM
    • +
    • Feedback
    • +
    • Bug tracking
    • +
    • Customer Q&A (StackExchange)
    • +
    • Follow-up emails to customers to offer assistance
    • +
    +
  • +
+

Maintenance

+

Over the lifetime of the site, you’ll likely pay more in maintenance costs than the upfront costs.

+
    +
  • Responding to user emails
  • +
  • Requests for info
  • +
  • Feedback about the site
  • +
  • Password resets?
  • +
  • Tracking bug reports and feature requests
  • +
  • Site improvements
  • +
  • Additional content
  • +
  • Moderation of user content +
      +
    • Spam removal
    • +
    +
  • +
  • Log analysis +
      +
    • Google Analytics
    • +
    +
  • +
  • Assessing advertising effectiveness
  • +
  • Analysis of revenues/profitability
  • +
  • Upgrades +
      +
    • New/improved functionality
    • +
    • Bug fixes
    • +
    • Upgraded infrastructure
    • +
    +
  • +
  • Down-time +
      +
    • Web host
    • +
    • Upgrades
    • +
    • Accidents
    • +
    • Bugs
    • +
    +
  • +
  • Backups +
      +
    • Testing restoring from backups
    • +
    +
  • +
  • Payments for services +
      +
    • Domain name registration – DO NOT LOSE YOUR NAME!
    • +
    • Web hosting
    • +
    • Marketing/advertising
    • +
    +
  • +
+

 

+]]>
+ http://blog.boochtek.com/2015/09/08/website-checklist-2/feed + 0 +
+ + Not Quite Callbacks + http://blog.boochtek.com/2015/06/22/not-quite-callbacks + http://blog.boochtek.com/2015/06/22/not-quite-callbacks#respond + Tue, 23 Jun 2015 04:55:17 +0000 + + + + + + http://blog.boochtek.com/?p=245 + Continue reading "Not Quite Callbacks"]]> + I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+]]>
+ http://blog.boochtek.com/2015/06/22/not-quite-callbacks/feed + 0 +
+ + Architectural Thoughts + http://blog.boochtek.com/2015/06/01/architectural-thoughts + http://blog.boochtek.com/2015/06/01/architectural-thoughts#respond + Tue, 02 Jun 2015 04:51:59 +0000 + + + + + + + http://blog.boochtek.com/?p=240 + Continue reading "Architectural Thoughts"]]> + I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+]]>
+ http://blog.boochtek.com/2015/06/01/architectural-thoughts/feed + 0 +
+ + Hexagonal Rails Controllers + http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers + http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers#respond + Tue, 24 Feb 2015 05:50:28 +0000 + + + + + + http://blog.boochtek.com/?p=202 + Continue reading "Hexagonal Rails Controllers"]]> + I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+]]>
+ http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers/feed + 0 +
+ + TDD Is Alive And Well + http://blog.boochtek.com/2014/05/05/tdd-is-alive-and-well + http://blog.boochtek.com/2014/05/05/tdd-is-alive-and-well#comments + Tue, 06 May 2014 04:50:07 +0000 + + + + + + http://blog.boochtek.com/?p=191 + Continue reading "TDD Is Alive And Well"]]> + I went to RailsConf this year, and the very first talk was a keynote by David Heinemeier Hansson (DHH), the creator of Ruby on Rails. The TL;DR of his talk was “TDD rarely has value”. He followed up with a blog post the next day, titled “TDD is dead. Long live testing.“, and 2 more posts. I think this line of thought is terribly misguided, and causing more harm than good. This article is my response.

+

First, I would like to address the good points of the talk. He said that programming is pseudoscience, and that people want to tell us that there’s a secret to being a better programmer. But what it really takes is working hard — reading a lot of code, writing a lot of code, and rewriting a lot of code. He’s right. And I also agree with him that you should forget about patterns for a while when learning to code. Beginners try to throw patterns at a problem instead of letting the patterns emerge where they’re supposed to.

+

I don’t completely agree that programming is a pseudoscience. In some ways it is, but I think it’s more of a craft. It’s a craft, because there’s a lot of science involved, but there’s also an art to doing it well. And like any craft, you’re always working to get better. So to respond to DHH’s stance that “software is more like poetry than physics”, I think it falls
+somewhere in between.

+

With regard to the software engineering practices we use, there really isn’t much science available, mostly because it’s a soft science. That is, it’s really hard to isolate a single variable when comparing code between projects. And nobody has the time or money to write the same code so many times that the differences would be statistically significant.

+

So we don’t have much science on TDD. But we do have some. Here’s a collection of several: StudiesOfTestDrivenDevelopment. And here’s one that explicitly looks are the difference between test-first and test-last: Does Test-Driven Development Really Improve Software Design Quality? What do these tell us? They tell us that TDD costs us about 10-30% in short-term productivity; reduces bugs by 30-90%, and decreases code complexity by about 30%. As Code Complete tells us (in section 20.5, with studies to back it up), improving quality reduces development costs. So, like most Agile practices, this is a case where spending a bit more time in the short term leads to time savings in the long term.

+

The more important lesson in the talk was that you have to do what works best for you and your situation. If TDD doesn’t give better results, then either find out how to make it give better results, or stop using it. As we often say in the Agile world, Agile doesn’t mean that you can stop using your brain. While I think TDD is appropriate in most situations, there are cases where it’s not worth the additional up-front cost. If the most important thing for your project is time-to-market, then not testing might be the right decision for you.

+

To me, TDD provides a bunch of benefits. First and foremost, TDD is a design discipline. It ensures that I think about how my code will be used before I think about how to implement it. This is very powerful in ensuring that the code is well-written from the perspective of other code using it.

+

Tested code provides confidence to be able to make changes without breaking things. If we write tests after the code, we’re less likely to write them. Tests written after the code also tend to test the implementation instead of the desired functionality. What we really want is tests written as a specification. With tests as a specification, we can come back later and understand why code was written. Without tests, or with poor tests, we can’t understand why the code is there; if we want to rewrite it, we don’t have the confidence that we’re not missing something. Writing tests first also ensures that we only write the code that is needed to implement the required functionality.

+

I’m not sure why DHH hasn’t “gotten” TDD. I’m not sure if it’s because he’s  a better coder than average, or if he just thinks in a different way than most of us. I think it’s partly because he doesn’t understand TDD, which he admitted might be the case. And I think he’s conflating TDD and unit testing.

+

DHH is influential in the developer community, especially those newer to Ruby and Rails. People listen to what he has to say. I was happy to see almost every other speaker made fun of DHH’s ideas, and most of the crowd knew better. But there will be a lot of others who will hear DHH, respect his opinions, and not give TDD the try that it deserves. And that’s sad, because it will lead to an overall reduction in code quality in the world.

+

Here are some other people’s thoughts on the matter:

+ +

 

+]]>
+ http://blog.boochtek.com/2014/05/05/tdd-is-alive-and-well/feed + 2 +
+ + Includable ActiveRecord + http://blog.boochtek.com/2014/02/10/includable-activerecord + http://blog.boochtek.com/2014/02/10/includable-activerecord#respond + Mon, 10 Feb 2014 18:09:53 +0000 + + + + + + http://blog.boochtek.com/?p=104 + Continue reading "Includable ActiveRecord"]]> + I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+]]>
+ http://blog.boochtek.com/2014/02/10/includable-activerecord/feed + 0 +
+ + Testing Rails Validators + http://blog.boochtek.com/2014/01/26/testing-rails-validators + http://blog.boochtek.com/2014/01/26/testing-rails-validators#respond + Mon, 27 Jan 2014 05:27:07 +0000 + + + + + + http://blog.boochtek.com/?p=93 + Continue reading "Testing Rails Validators"]]> + It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+]]>
+ http://blog.boochtek.com/2014/01/26/testing-rails-validators/feed + 0 +
+
+
diff --git a/public/category/webdev/rails.html b/public/category/webdev/rails.html new file mode 100644 index 0000000..b93b20a --- /dev/null +++ b/public/category/webdev/rails.html @@ -0,0 +1,408 @@ + + + + + + + + + + + + + + + +Ruby on Rails – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Not Quite Callbacks

+ + + +
+

I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+
+ + +
+ +
+
+ +

Hexagonal Rails Controllers

+ + + +
+

I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+
+ + +
+ +
+
+ +

TDD Is Alive And Well

+ + + +
+

I went to RailsConf this year, and the very first talk was a keynote by David Heinemeier Hansson (DHH), the creator of Ruby on Rails. The TL;DR of his talk was “TDD rarely has value”. He followed up with a blog post the next day, titled “TDD is dead. Long live testing.“, and 2 more posts. I think this line of thought is terribly misguided, and causing more harm than good. This article is my response.

+

First, I would like to address the good points of the talk. He said that programming is pseudoscience, and that people want to tell us that there’s a secret to being a better programmer. But what it really takes is working hard — reading a lot of code, writing a lot of code, and rewriting a lot of code. He’s right. And I also agree with him that you should forget about patterns for a while when learning to code. Beginners try to throw patterns at a problem instead of letting the patterns emerge where they’re supposed to.

+

I don’t completely agree that programming is a pseudoscience. In some ways it is, but I think it’s more of a craft. It’s a craft, because there’s a lot of science involved, but there’s also an art to doing it well. And like any craft, you’re always working to get better. So to respond to DHH’s stance that “software is more like poetry than physics”, I think it falls
+somewhere in between.

+

With regard to the software engineering practices we use, there really isn’t much science available, mostly because it’s a soft science. That is, it’s really hard to isolate a single variable when comparing code between projects. And nobody has the time or money to write the same code so many times that the differences would be statistically significant.

+

So we don’t have much science on TDD. But we do have some. Here’s a collection of several: StudiesOfTestDrivenDevelopment. And here’s one that explicitly looks are the difference between test-first and test-last: Does Test-Driven Development Really Improve Software Design Quality? What do these tell us? They tell us that TDD costs us about 10-30% in short-term productivity; reduces bugs by 30-90%, and decreases code complexity by about 30%. As Code Complete tells us (in section 20.5, with studies to back it up), improving quality reduces development costs. So, like most Agile practices, this is a case where spending a bit more time in the short term leads to time savings in the long term.

+

The more important lesson in the talk was that you have to do what works best for you and your situation. If TDD doesn’t give better results, then either find out how to make it give better results, or stop using it. As we often say in the Agile world, Agile doesn’t mean that you can stop using your brain. While I think TDD is appropriate in most situations, there are cases where it’s not worth the additional up-front cost. If the most important thing for your project is time-to-market, then not testing might be the right decision for you.

+

To me, TDD provides a bunch of benefits. First and foremost, TDD is a design discipline. It ensures that I think about how my code will be used before I think about how to implement it. This is very powerful in ensuring that the code is well-written from the perspective of other code using it.

+

Tested code provides confidence to be able to make changes without breaking things. If we write tests after the code, we’re less likely to write them. Tests written after the code also tend to test the implementation instead of the desired functionality. What we really want is tests written as a specification. With tests as a specification, we can come back later and understand why code was written. Without tests, or with poor tests, we can’t understand why the code is there; if we want to rewrite it, we don’t have the confidence that we’re not missing something. Writing tests first also ensures that we only write the code that is needed to implement the required functionality.

+

I’m not sure why DHH hasn’t “gotten” TDD. I’m not sure if it’s because he’s  a better coder than average, or if he just thinks in a different way than most of us. I think it’s partly because he doesn’t understand TDD, which he admitted might be the case. And I think he’s conflating TDD and unit testing.

+

DHH is influential in the developer community, especially those newer to Ruby and Rails. People listen to what he has to say. I was happy to see almost every other speaker made fun of DHH’s ideas, and most of the crowd knew better. But there will be a lot of others who will hear DHH, respect his opinions, and not give TDD the try that it deserves. And that’s sad, because it will lead to an overall reduction in code quality in the world.

+

Here are some other people’s thoughts on the matter:

+ +

 

+
+ + +
+ +
+
+ +

Includable ActiveRecord

+ + + +
+

I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+
+ + +
+ +
+
+ +

Testing Rails Validators

+ + + +
+

It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/category/webdev/rails/feed b/public/category/webdev/rails/feed new file mode 100644 index 0000000..e224fde --- /dev/null +++ b/public/category/webdev/rails/feed @@ -0,0 +1,249 @@ + + + + Ruby on Rails – BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.5.3 + + Not Quite Callbacks + http://blog.boochtek.com/2015/06/22/not-quite-callbacks + http://blog.boochtek.com/2015/06/22/not-quite-callbacks#respond + Tue, 23 Jun 2015 04:55:17 +0000 + + + + + + http://blog.boochtek.com/?p=245 + Continue reading "Not Quite Callbacks"]]> + I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+]]>
+ http://blog.boochtek.com/2015/06/22/not-quite-callbacks/feed + 0 +
+ + Hexagonal Rails Controllers + http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers + http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers#respond + Tue, 24 Feb 2015 05:50:28 +0000 + + + + + + http://blog.boochtek.com/?p=202 + Continue reading "Hexagonal Rails Controllers"]]> + I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+]]>
+ http://blog.boochtek.com/2015/02/23/hexagonal-rails-controllers/feed + 0 +
+ + TDD Is Alive And Well + http://blog.boochtek.com/2014/05/05/tdd-is-alive-and-well + http://blog.boochtek.com/2014/05/05/tdd-is-alive-and-well#comments + Tue, 06 May 2014 04:50:07 +0000 + + + + + + http://blog.boochtek.com/?p=191 + Continue reading "TDD Is Alive And Well"]]> + I went to RailsConf this year, and the very first talk was a keynote by David Heinemeier Hansson (DHH), the creator of Ruby on Rails. The TL;DR of his talk was “TDD rarely has value”. He followed up with a blog post the next day, titled “TDD is dead. Long live testing.“, and 2 more posts. I think this line of thought is terribly misguided, and causing more harm than good. This article is my response.

+

First, I would like to address the good points of the talk. He said that programming is pseudoscience, and that people want to tell us that there’s a secret to being a better programmer. But what it really takes is working hard — reading a lot of code, writing a lot of code, and rewriting a lot of code. He’s right. And I also agree with him that you should forget about patterns for a while when learning to code. Beginners try to throw patterns at a problem instead of letting the patterns emerge where they’re supposed to.

+

I don’t completely agree that programming is a pseudoscience. In some ways it is, but I think it’s more of a craft. It’s a craft, because there’s a lot of science involved, but there’s also an art to doing it well. And like any craft, you’re always working to get better. So to respond to DHH’s stance that “software is more like poetry than physics”, I think it falls
+somewhere in between.

+

With regard to the software engineering practices we use, there really isn’t much science available, mostly because it’s a soft science. That is, it’s really hard to isolate a single variable when comparing code between projects. And nobody has the time or money to write the same code so many times that the differences would be statistically significant.

+

So we don’t have much science on TDD. But we do have some. Here’s a collection of several: StudiesOfTestDrivenDevelopment. And here’s one that explicitly looks are the difference between test-first and test-last: Does Test-Driven Development Really Improve Software Design Quality? What do these tell us? They tell us that TDD costs us about 10-30% in short-term productivity; reduces bugs by 30-90%, and decreases code complexity by about 30%. As Code Complete tells us (in section 20.5, with studies to back it up), improving quality reduces development costs. So, like most Agile practices, this is a case where spending a bit more time in the short term leads to time savings in the long term.

+

The more important lesson in the talk was that you have to do what works best for you and your situation. If TDD doesn’t give better results, then either find out how to make it give better results, or stop using it. As we often say in the Agile world, Agile doesn’t mean that you can stop using your brain. While I think TDD is appropriate in most situations, there are cases where it’s not worth the additional up-front cost. If the most important thing for your project is time-to-market, then not testing might be the right decision for you.

+

To me, TDD provides a bunch of benefits. First and foremost, TDD is a design discipline. It ensures that I think about how my code will be used before I think about how to implement it. This is very powerful in ensuring that the code is well-written from the perspective of other code using it.

+

Tested code provides confidence to be able to make changes without breaking things. If we write tests after the code, we’re less likely to write them. Tests written after the code also tend to test the implementation instead of the desired functionality. What we really want is tests written as a specification. With tests as a specification, we can come back later and understand why code was written. Without tests, or with poor tests, we can’t understand why the code is there; if we want to rewrite it, we don’t have the confidence that we’re not missing something. Writing tests first also ensures that we only write the code that is needed to implement the required functionality.

+

I’m not sure why DHH hasn’t “gotten” TDD. I’m not sure if it’s because he’s  a better coder than average, or if he just thinks in a different way than most of us. I think it’s partly because he doesn’t understand TDD, which he admitted might be the case. And I think he’s conflating TDD and unit testing.

+

DHH is influential in the developer community, especially those newer to Ruby and Rails. People listen to what he has to say. I was happy to see almost every other speaker made fun of DHH’s ideas, and most of the crowd knew better. But there will be a lot of others who will hear DHH, respect his opinions, and not give TDD the try that it deserves. And that’s sad, because it will lead to an overall reduction in code quality in the world.

+

Here are some other people’s thoughts on the matter:

+ +

 

+]]>
+ http://blog.boochtek.com/2014/05/05/tdd-is-alive-and-well/feed + 2 +
+ + Includable ActiveRecord + http://blog.boochtek.com/2014/02/10/includable-activerecord + http://blog.boochtek.com/2014/02/10/includable-activerecord#respond + Mon, 10 Feb 2014 18:09:53 +0000 + + + + + + http://blog.boochtek.com/?p=104 + Continue reading "Includable ActiveRecord"]]> + I created a Ruby gem recently, called includable-activerecord. It’s pretty small, but I thought I might explain why I created it, and discuss its implementation.

+

Classical Inheritance

+

When you use ActiveRecord, you normally include it in your model like this:

+
class User < ActiveRecord::Base
+  # ...
+end
+

Your User class is inheriting from the ActiveRecord::Base class. This is class-based inheritance, also called “classical” inheritance. (That’s “classical” as in “class”, not as a synonym for “traditional”.) Class-based inheritance represents an “is-a” relationship. So we’re saying that a user is an ActiveRecord base. Another way to say this is that User is a subclass of ActiveRecord::Base.
+

+There are a few problems with this. First, what is a “base”? The name was chosen because it’s a base class. But just like we don’t give factory classes names like UserFactory (at least not in Ruby), we shouldn’t name base classes Base.

+

I suppose that we’re trying to say that this is an ActiveRecord model. That sounds fine at first glance — this is our model for users. But what if we also want to say that a user is a person? Ruby doesn’t allow inheriting from multiple classes. Now we have to choose whether to inherit from ActiveRecord::Base or Person. Person makes more sense, because it fills the “is-a” rule better. Class inheritance is intended for a hierarchical “is-a” relationship, such as “a user is a person”, or “a circle is a shape”. But since ActiveRecord::Base is a base class, we have to use it as our base class.

+

We could work around this problem by subclassing Person from ActiveRecord::Base and then subclassing User from Person. That’s fine if Person is also a model that we store in the database. But if that’s not the case, then we have a problem.

+

Mixins

+

Ruby provides another way of implementing inheritance — mixins. We often don’t think of this as an inheritance model, but it really is. When we include a module, that module gets added to the class’s ancestor chain. We can mix in as many modules as we want.

+

Mixins indicate more of an “acts like” relationship than an “is-a” relationship. It’s for shared behavior between classes that don’t have a hierarchical relationship. For example, when we mix in the Enumerable module, we’re saying that we want our class to act like other classes that include Enumerable. That sounds more like what we want ActiveRecord to be. We want our user model to behave like other ActiveRecord models, in the way that they can persist to a database.

+

But ActiveRecord doesn’t support that. Almost all the other Ruby ORMs do; as we’ve shown above, this is for good reasons.

+

Implementation

+

So I decided to see if I could implement the equivalent of the ActiveRecord::Base class as a module that could be mixed into model classes. I decided to call my mixin module ActiveRecord::Model, because classes that mix it in will behave as ActiveRecord models.

+

It turns out that ActiveRecord::Base is a pretty complex class. It includes and extends a lot of other modules. Luckily, as of ActiveRecord 4.0, that’s all the code it includes.

+

The module only defines a single class method, included. This is one of Ruby’s many hook methods. It gets called when the module in question gets included in another module, and receives that other model as its argument. All we need to have this method do is to include everything that ActiveRecord::Base includes, and extend everything that ActiveRecord::Base extends. Ruby provides a method that’s defined on all classes, called included_modules, which we can use to get the list of everything that’s included in ActiveRecord::Base. Unfortunately, there’s no equivalent list of extended_modules. But a quick search on Stack Overflow found an implementation of extended_modules that we could use.

+

So with a bit of magic (i.e. hooks and meta-programming), we can get the lists of constituent modules from the ActiveRecord::Base class, and include them in our ActiveRecord::Model module.

+

So with all that, we can now include the includable-activerecord gem and mix it in, with all the advantages that provides:

+
class User
+  include ActiveRecord::Model
+  # ...
+end
+

It was exciting to be able to make this work. Since I wrote it as a proof of concept, I haven’t written any tests yet. But it seems to be working just fine. The main thing I really need to look into is making sure that plugins that extend ActiveRecord::Base from their own code will still work. I’m pretty sure this will work out of the box, because the ActiveRecord::Model.included doesn’t run until the model class is loaded, and that happens after those plugins have initialized themselves.

+]]>
+ http://blog.boochtek.com/2014/02/10/includable-activerecord/feed + 0 +
+ + Testing Rails Validators + http://blog.boochtek.com/2014/01/26/testing-rails-validators + http://blog.boochtek.com/2014/01/26/testing-rails-validators#respond + Mon, 27 Jan 2014 05:27:07 +0000 + + + + + + http://blog.boochtek.com/?p=93 + Continue reading "Testing Rails Validators"]]> + It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+]]>
+ http://blog.boochtek.com/2014/01/26/testing-rails-validators/feed + 0 +
+
+
diff --git a/public/comments/feed b/public/comments/feed new file mode 100644 index 0000000..fc439c1 --- /dev/null +++ b/public/comments/feed @@ -0,0 +1,138 @@ + + + Comments for BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Wed, 21 Jun 2017 01:27:18 +0000 + hourly + 1 + https://wordpress.org/?v=4.7.2 + + Comment on Chording Keyers by Wayne Rasanen + http://blog.boochtek.com/2014/02/23/chording-keyers/comment-page-1#comment-61468 + + Wed, 21 Jun 2017 01:27:18 +0000 + http://blog.boochtek.com/?p=124#comment-61468 + + I’m glad you were able to get the DecaTxt connected and working. We are planning for a second version with micro USB rather than mini and a few software changes like being able to shut off the Bluetooth antenna. I’d love to get your feedback if you would email me and offer suggestions.
+Thanks,
+Wayne

+]]>
+
+ + Comment on You Don’t Have to Be Right by You Can’t Handle the Truth (Trust, Truth and Transparency) – 114 | This Agile Life + http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right/comment-page-1#comment-60403 + + Tue, 26 Jul 2016 02:52:11 +0000 + http://blog.boochtek.com/?p=313#comment-60403 + + […] You Don’t Have to Be Right blog article by myself […]

+]]>
+
+ + Comment on You Don’t Have to Be Right by yves + http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right/comment-page-1#comment-60360 + + Mon, 18 Jul 2016 23:15:22 +0000 + http://blog.boochtek.com/?p=313#comment-60360 + + Hello, I have read your ideas about the Brilliant Programming Language.

+

I was wondering if you had made any progress with it? It seems like a language I would enjoy working with.

+

Best of luck.

+]]>
+
+ + Comment on You Don’t Have to Be Right by Five Blogs – 13 July 2016 – 5blogs + http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right/comment-page-1#comment-60342 + + Wed, 13 Jul 2016 05:03:15 +0000 + http://blog.boochtek.com/?p=313#comment-60342 + + […] You Don’t Have to Be Right Written by: Craig Buchek […]

+]]>
+
+ + Comment on Chording Keyers by Oisín + http://blog.boochtek.com/2014/02/23/chording-keyers/comment-page-1#comment-60320 + + Tue, 28 Jun 2016 22:02:28 +0000 + http://blog.boochtek.com/?p=124#comment-60320 + + Update: It works just fine! Actually the problem was that my laptop only supports Bluetooth 3.0, but at least 4.0 is needed. It also wasn’t discoverable by my phone because Android 4.3 or newer is needed. Tested it on a newer phone and on my work PC and it connected straight away. Looking forward to trying it on an Android tablet and measuring my speed over a few weeks.

+]]>
+
+ + Comment on Chording Keyers by Oisín + http://blog.boochtek.com/2014/02/23/chording-keyers/comment-page-1#comment-60319 + + Mon, 27 Jun 2016 23:15:39 +0000 + http://blog.boochtek.com/?p=124#comment-60319 + + The In10did DecaTxt is available on eBay and Amazon now. I ordered one recently and had it shipped over from the USA, which was a bit expensive when customs fees are added. It arrived yesterday morning and I plugged it in after arriving home from work. Sadly, it appears to do nothing whatsoever. Not sure if this model is DOA (but surely they test them?), or what. If I can get it to work, or get a replacement shipped, I’ll try to record my WPM with the device every day for a couple of weeks, to see where the improvement curve starts to taper off.

+]]>
+
+ + Comment on My Thoughts on Python vs. Ruby by pete + http://blog.boochtek.com/2013/02/22/my-thoughts-on-python-vs-ruby/comment-page-1#comment-59859 + + Fri, 13 May 2016 12:50:02 +0000 + http://blog.boochtek.com/?p=76#comment-59859 + + I feel like the lesson here is: Yes, if you come to Python expecting Ruby, you’re gonna have a bad time.

+]]>
+
+ + Comment on The Problem With Estimates by martin + http://blog.boochtek.com/2015/09/28/no-estimates/comment-page-1#comment-56383 + + Thu, 03 Dec 2015 08:53:51 +0000 + http://blog.boochtek.com/?p=261#comment-56383 + + great read, thank you for posting.

+]]>
+
+ + Comment on The Problem With Estimates by Fredrik + http://blog.boochtek.com/2015/09/28/no-estimates/comment-page-1#comment-56304 + + Mon, 30 Nov 2015 19:37:33 +0000 + http://blog.boochtek.com/?p=261#comment-56304 + + “I’ve never seen a case where a project is completed more quickly than estimated.”

+

Really?

+]]>
+
+ + Comment on When Should We Do TDD? by Dennis Nerush + http://blog.boochtek.com/2015/05/11/when-tdd/comment-page-1#comment-56128 + + Wed, 25 Nov 2015 20:25:48 +0000 + http://blog.boochtek.com/?p=225#comment-56128 + + Applying TDD in Your Company is **More Important than Ever**!
+This methodology will increase your product’s quality and will allow you (after a while) to deliver features faster.

+

Here is why –
+http://dennis-nerush.blogspot.co.il/2015/11/applying-tdd-in-your-company-is-more.html

+]]>
+
+
+
diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/public/feed b/public/feed new file mode 100644 index 0000000..a3da0c1 --- /dev/null +++ b/public/feed @@ -0,0 +1,311 @@ + + + + BoochTek, LLC + + http://blog.boochtek.com + Web Development, Ruby on Rails, Open Source + Thu, 07 Jul 2016 04:22:08 +0000 + en-US + hourly + 1 + https://wordpress.org/?v=4.7.2 + + You Don’t Have to Be Right + http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right + http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right#comments + Thu, 07 Jul 2016 04:22:08 +0000 + + + + + http://blog.boochtek.com/?p=313 + Continue reading "You Don’t Have to Be Right"]]> + Not too long ago, I was asked during a job interview “how do you convince your teammates that you’re right?” I answered with probably the most surprising answer: “I don’t.”

+

It’s taken me years to realize that being right isn’t terribly important. Especially when there’s more than one right answer — which there usually is. It’s more important to work as a team. It’s more important to be respected and to have respect for others.

+

A few weeks after that interview, I was pair programming with the person who had asked me the question. We’re both senior developers, so we both have a lot of experience and opinions based on that experience. We were writing a shell script, and we both had an idea in mind about how to write the code. I let him proceed with his idea. It was a pretty good idea; probably better than mine. But we paired very effectively. I’d let him finish some code, and I’d find a way to make it better. Then he’d find a way to make my code better. What we ended up with was so much better than my original idea, and his original idea as well. I commented on this, and he agreed. We left that pairing session feeling the high of having accomplished something rare — two very experience programmers coming up with better code than we could have imagined going in.

+

I like to think of writing code as a path towards a destination. The destination is good code that does what it’s supposed to, is readable (intention revealing), is concise, and is well-factored so as to be easy to change in the future. But there are many paths to that destination. Especially when pair programming, the code gets refined as ideas are shared, getting you closer and closer to that destination. Sometimes the different paths end up with the same code; sometimes they end up with different code. But if the code does its job well (according to those criteria), it doesn’t really matter what code you ended up with.

+

Thinking back to the original question, I think the answer is a little more nuanced. (I’m pretty sure I followed up with an explanation, but I don’t recall the details.) It truly doesn’t matter in a lot of cases. If there are different paths that lead to the same destination, and they’re all roughly equal, then my rule works. It can also work even if the first path you try (a “wrong” path) doesn’t work out, as long as it’s easy enough to try a different path. I find that the majority of day-to-day coding fits these conditions.

+

But there are some places where it’s costly to get things wrong the first time. In these cases, it’s worth spending some time thinking about and discussing the alternatives before getting started. Software architecture is the big one that comes to mind. In general, weighing the pros and cons of each option and applying previous experience works best.

+

So next time you think about trying to convince someone you’re right, fight the urge. Instead, let them show you what they’re thinking. They just might surprise you. And more importantly, you might surprise yourself.

+]]>
+ http://blog.boochtek.com/2016/07/06/you-dont-have-to-be-right/feed + 3 +
+ + My First Open Space + http://blog.boochtek.com/2016/02/10/first-open-space + http://blog.boochtek.com/2016/02/10/first-open-space#respond + Thu, 11 Feb 2016 05:42:54 +0000 + + + + http://blog.boochtek.com/?p=307 + Continue reading "My First Open Space"]]> + I recently attended an Open Space hosted at work. I’d never been to an Open Space, and didn’t know what to expect. We’d been told that this was a workshop to help Engineering Managers (my role), Product Owners, and Product Analysts find better ways of working together. But due to the way an Open Space works, it evolved into something completely different — and better.

+

We’d brought in Diana Larsen to facilitate the Open Space. Diana is a stalwart of the Agile community, focusing on how people and teams interact. She literally (co-)wrote the book on Agile retrospectives. Diana was also kind enough to be our guest at an Agile LINC meetup later in the evening.

+

The morning started off with all the participants sitting in chairs arranged in a circle. Diana rang a chime, a nice soothing tone that literally set the tone for the day. (Most people didn’t seem to like it, but I thought it had its purpose.) It also acted as a call to gather in the meeting space. She then walked around the inside of the circle as she explained what was going to happen.

+

The idea behind Open Spaces came with the realization that at a conference, the “hallway track” (ad hoc discussions in the hallways) was often more valuable than the scheduled talks. So they figured out a way to capture that experience. There are only a few rules:

+
    +
  • Whoever shows up is the right group
  • +
  • Whatever happens is the only thing that could have
  • +
  • Be prepared to be surprised
  • +
  • Whenever it starts is the right time
  • +
  • When it’s over, it’s over
  • +
  • The Law of 2 Feet: If you’re not learning or contributing, go somewhere else
  • +
+

Once Diana set the scene and explained the rules, people came up and presented ideas for topics. If you proposed a topic, you chose a time and location on the topic board. At that time, you’d facilitate a discussion on that topic. The format was really conducive to discussions. There was no preparation, so discussions were from the heart — lots of people felt that they could contribute.

+

Being new to the company (only 2 months), I got a lot out of the workshop, beyond the content itself. I got to get to know several more people. I’d even say I made a few friends. Having open discussions, we found that many of the teams were having the same issues. This made it easy for me to talk to other people.

+

All-in-all, I found the Open Space to be extremely conducive to discussions aimed at identifying issues, brainstorming solutions, and planning action items. I felt like we made great strides in addressing some of the biggest problems our organization is currently facing.

+]]>
+ http://blog.boochtek.com/2016/02/10/first-open-space/feed + 0 +
+ + Resolutions for 2016 + http://blog.boochtek.com/2016/01/19/resolutions-2016 + http://blog.boochtek.com/2016/01/19/resolutions-2016#respond + Wed, 20 Jan 2016 05:51:09 +0000 + + + + http://blog.boochtek.com/?p=290 + Continue reading "Resolutions for 2016"]]> + I’ve written up a list of career-related resolutions the past few years. I’ve been pretty successful meeting those goals, so I’m going to continue the tradition.

+

Coding

+

I want to increase the Open Source code I write this year. That’s basically everything I write that’s not directly related to work.

+

I took over maintenance of Virtus late last year, but haven’t done a good job at finding the time for that. That’s probably more responding to issues on GitHub and merging pull requests. But eventually, I’ll likely add some features and do some refactoring.

+

My goal is to work on 6 apps and libraries this year. That’s a pretty aggressive goal, but I want to focus on both learning new things and building some simple but useful things. Think MVP — focus on getting a lot of bang for not too much effort.

+

I want to work on a new Rails app, to familiarize myself with Rails 5. I want to get some experience with some more modern gems. I also want to see if I can use some new techniques with a Rails app, especially things that might help us get closer to a hexagonal architecture. I’m also hoping to work with some different web frameworks — maybe Lotus, Rodakase, Trailblazer, or Phoenix. Or maybe one of the Crystal web frameworks.

+

I also want to work on a couple Elm apps. I started the STL Elm group as an excuse to learn the language. I’d like to work on a Twitter client, that might eventually turn into a more general reading app. Beyond Twitter, it would keep track of things I want to read. The other Elm app I’d like to write is a game, based on the old Space Taxi game I loved to play on the Commodore 64.

+

I wrote a micro-ORM last year called Ruby Preserves. I’ve started converting that from working with raw SQL to sitting atop the awesome Sequel gem. The simplicity is still there so far, and basing it on Sequel has so far gone easier than I expected. But I haven’t figured out how to do relationships (JOINs) yet; that will be the real test.

+

Conferences

+

I’m pretty happy with my 2015 conference experience. I want to keep my conference level about the same. I don’t really want to commit to more talks than I gave last year. Conference presentations take a lot of time and energy to write and practice.

+

I’m planning to go to RailsConf, RubyConf, and Strange Loop. I’m hoping to give a talk at each of those, if I can. But I’ll probably attend all of them even if I’m not speaking. I’m also submitting a talk to Agile 2016. I’m unlikely to go to any other conferences, and even more unlikely to give more than 4 conference talks.

+

Writing

+

I really want to finish my Effective Agile book this year. I’m going to try to pull Amos in to pair with me on it occasionally, to move it forward. If I’m able to get it done, I’d also like to maybe write a book on Elm.

+

I actually want to do less blogging this year. Well, sort of. I want to put coding and book writing ahead of blogging. If that means less blogging, I’m okay with that. So my goal for blogging is more like every other week. And when I do write a blog article, I’d rather it be related to some code I’m working on, something that I could use in my book, or some more in-depth thoughts about programming language design.

+

Job Hunting

+

The contract I recently started at CenturyLink Cloud is open-ended, so I’ll likely be there for the entire year. I’m enjoying it so far; it’s a challenge, but I think I can make a significant impact. However, I’m really interested in moving to San Francisco in 2017. So I’m going to work on preparing for that in several ways.

+

I’m going to resurrect my LinkedIn account. I’ve neglected it for a few years now. I’ll make sure everything is up to date, and make some connections I’ve been putting off. I also want to reclaim my Stack Exchange and Hacker News accounts.

+

I’m going to pay a an expert to help me revamp my résumé. I think it’s pretty decent, but it could use some freshening up. I want it to stand out. I want to do a better job of explaining what I really do — which is to join a team to help them improve their processes and their technical skills.

+

I’ll also need to clean up all my web sites, so they look nice when people come to take a look at them. This includes my personal site, consulting business site, wiki, and blogs. I should also upgrade my server to the latest version of Debian, and use Ansible and some other tools to provision it using the principles of Infrastructure as Code.

+]]>
+ http://blog.boochtek.com/2016/01/19/resolutions-2016/feed + 0 +
+ + Team Values + http://blog.boochtek.com/2016/01/04/team-values + http://blog.boochtek.com/2016/01/04/team-values#respond + Tue, 05 Jan 2016 04:56:46 +0000 + + + + + http://blog.boochtek.com/?p=294 + Continue reading "Team Values"]]> + I held a retrospective with my new team last week. The team includes 2 senior developers, 2 junior developers, a product owner, and a product analyst. I’ve joined the team as an engineering manager, which I think of more as a team lead with an elevated title. Being new to this group, I wanted a way to understand their values. What motivates them? What common values do we share that we can leverage to move forward in the same direction?

+

I started out with a pretty simple question: “What do you value (in regards to what we’re building); what are you willing to fight for?” I asked them each to write down several values and then put them on the board before looking at everyone else’s answers. I also asked each person to rank their values in order of importance.

+

It turned out that my question was a little vague. Some people thought about the question in terms of the end result, and some in terms of the process of creating the software. In some ways that was a bit of a problem, because the different interpretations led people in different directions. But in other ways, not pushing them in any particular direction got more varied answers, exposing how they think about the project and the product.

+

My list (in order) was Effectiveness (doing the right thing), Quality (doing things right the first time), Happiness, Purpose, and Teamwork. In retrospect, I should have put Happiness first. If I’m not happy at work, I don’t really want to be there, and need to move on. (Sometimes I can trade some happiness at work for more happiness at home, but I’m definitely a person that needs to be happy at work.) The other values serve to improve my happiness, but the happiness is more important.

+

My own issue ranking my values turned out to be a problem with the ranking in general. Should they be ranked in importance of the necessity of the value as an outcome, or in importance of the necessity to focus on the value? I think the former is what I was looking for, but even I wasn’t clear on that when I began the exercise.

+

After everyone put their values up on the board, we read them off. Then I asked the team to choose several values that we share as a team. We pulled them off the individual members’ lists, and put them in the team list. Then I asked each person to come up and rank those values, then explain why they had ordered them that way, especially when they ordered them significantly different than the last person. I was hoping to come to some convergence of the rank over time, so we could document our values in rank order. That didn’t happen. But the discussion was illuminating to me and to everyone on the team.

+

I think the most interesting part about the lack of convergence was the difference between the developers and the product guys. The product guys definitely viewed the values more in terms of outcomes than the process. That makes sense — they’re not as intimately involved in the process of building the product.

+

We were able to converge on the top priority though: Will the user buy the product? This was a combination of a couple different values that we merged together. This included the end user experience as well as making sure the team would continue to have a reason for existing. The rest of the values we left unordered: Teamwork (cohesiveness), Data driven decisions, Team ownership, Simplicity, Effectiveness, Maintainability / Supportability, Quality, Automation, and Performance. I think that’s a pretty decent list.

+

As a couple teammates pointed out, those values are probably in part a reflection of this current point in time. If I asked the same question some other time, under different conditions and team dynamics, the answers would probably change a bit. And we’d probably come up with other answers if asked again, just due to randomness of the way we think about these things.

+

But I don’t think I’d do this activity a second time with the team. It was really about understanding our motivations — both our own, and those of our teammates. I found it effective in that way, and also in helping the team to think about our culture and how we can work to shape it to help us all push in the same direction.

+

There are a few caveats. When I asked for feedback on the exercise, one teammate pointed out that it wouldn’t work if people weren’t honest about their values, and they answered with what they thought management or their teammates wanted to hear. I don’t think that was an issue with this group, but it’s something to keep in mind.

+

The other major thing I’d do is to make it clear up front that I’m not looking for any action items from this activity; it’s more about understanding each other and ourselves. And next time, I’ll work to clarify how to rank the values.

+

I would recommend this activity for a new team, or when the makeup of the team is changing in some significant way. I wish there was a way to help a team converge on the ranking of their values, but I suppose I should be happy that agreeing on the set of important values went pretty quickly. And the diversity of ideas and opinions is probably a blessing that I should be embracing — the more ideas we have, the wider the variety of solutions we can imagine.

+]]>
+ http://blog.boochtek.com/2016/01/04/team-values/feed + 0 +
+ + 2015 Year in Review + http://blog.boochtek.com/2015/12/28/year-in-review-2015 + http://blog.boochtek.com/2015/12/28/year-in-review-2015#respond + Tue, 29 Dec 2015 05:28:38 +0000 + + + + + + + + http://blog.boochtek.com/?p=287 + Continue reading "2015 Year in Review"]]> + It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+]]>
+ http://blog.boochtek.com/2015/12/28/year-in-review-2015/feed + 0 +
+ + Face Your Fears + http://blog.boochtek.com/2015/12/21/face-your-fears + http://blog.boochtek.com/2015/12/21/face-your-fears#respond + Tue, 22 Dec 2015 05:38:29 +0000 + + + + http://blog.boochtek.com/?p=285 + Continue reading "Face Your Fears"]]> + I’ve always been someone who faces my fears.

+

I have a moderate case of arachnophobia. I don’t run away when I see a spider, but it creeps my out when one is crawling on me. When I was in college, I decided to buy a tarantula to attempt to get over my irrational fear of spiders. I thought I’d be able to get more comfortable with the tarantula over time, eventually to the point of letting it crawl on my arm. It didn’t work. Although I did find that my fear of tarantulas is rational — I got a terrible case of hives just from touching the urticating hairs that fell off into its water sponge.

+

Last week, I was on vacation in Mexico. One of the excursions we took involved jumping in the water a lot. I’m not a strong swimmer — mostly because I have a hard time closing my nose; I hold my nose when I jump in. We zip-lined into the water a lot. At one point, there was a cliff to dive into the water from. It was about 15 feet above the water. It didn’t look so far down before jumping. But it felt like a really long way down the first time I jumped from it. It was pretty scary for me. So I did it a second time. There wasn’t any peer pressure to jump a second time. I literally jumped a second time specifically because I was scared.

+

Fear is a weird thing. Fear is there to protect us. But it’s there to protect us from the dangers of the African savannah. Most of the things our fears protect us from don’t exist in our everyday modern lives. So maybe we should work to gain a better understanding of how our fears work, to figure out when to pay attention to them and when to ignore them.

+

Fortunately, our brain has a good mechanism to help us do this. Our brains basically have 2 main processing systems. The first one is for quick reactions. This one involves things that are nearly reflexes. Fear is in this system. The second system is our analytical reasoning system. This system takes longer to process, but is able to take on more information.

+

Whenever the situation allows us time for both systems to work, we need to listen to them both. We need to listen to our fears, because they’re there for a reason. But that reason might not pertain to our situation. So we need to realize that, and let the slow analytical system determine if we should ignore our fears.

+

If we don’t allow both systems to work, we’re not taking full advantage of our brains; we’re not taking full advantage of the situations that life is presenting to us.

+]]>
+ http://blog.boochtek.com/2015/12/21/face-your-fears/feed + 0 +
+ + Encouragement + http://blog.boochtek.com/2015/12/15/encouragement + http://blog.boochtek.com/2015/12/15/encouragement#respond + Wed, 16 Dec 2015 05:00:22 +0000 + + + + http://blog.boochtek.com/?p=282 + Continue reading "Encouragement"]]> + I’ve been on vacation the past week, in Cozumel, Mexico. One day, we went on an excursion called Xenotes. A cenote (say-NO-tay) is a sinkhole filled with fresh water. (The “X” is to give it a more Mayan-sounding trademarkable name.) We had a lot of fun swimming, kayaking, and zip-lining. A bilingual tour guide led our group, which consisted of people from across the US and South America, of various ages and physical abilities.

+

Our tour guide was a lot of fun. He made jokes, told us about the cenotes, and led us in the activities. But he also encouraged us. When someone was scared to do something, he was supportive. He told us that it was okay, and we could do it. But we also felt like it was OK to fail, if we really couldn’t. It really felt like his encouragement was literally creating courage.

+

What was really neat was that despite the language barrier, everyone else was also supportive and encouraging. Everyone cheered with encouragement before someone would attempt something difficult. And we’d cheer especially loud once someone accomplished something that was difficult for their abilities.

+

It was an awesome feeling to feel so supported. It made me feel like I was in a safe place, where I could try new things that were a little past my comfort zone. I was able to do the zip-line upside-down. I jumped off a 15-foot cliff into the water. I even jumped off the cliff a second time, even though I was a little scared.

+

Back at the resort, we played some volleyball in the pool. It was a similar situation, with players of varying ages and abilities. Again, we tried to help the weaker players feel comfortable so they could improve without feeling judged or self-conscious. It helped everyone have a good time. It made everything more fun to be in such a supportive environment, whether I was in a position as one of the more skilled (volleyball), or one of the less skilled (jumping or zip-lining into the water). We were all able to accomplish more, with less effort.

+

These experiences provide a good lesson that can be applied in a lot of places. Such a supportive environment would help any relationship, and any team. I’ve been on a couple really good software development teams, but I don’t think any of them have been as supportive as these two groups of strangers.

+

I’ve decided that this should be one of my goals as a team leader. I want to create an encouraging environment for the whole team. I want to make sure that everyone is comfortable enough that they feel like they can try things that are difficult, even if they might fail (as long as nobody gets hurt).

+

If a group of strangers can do this, so can your team. So can you and your significant others. We need to work every day to make sure that we’re supporting each other. It’s the best way to get everyone to achieve more.

+]]>
+ http://blog.boochtek.com/2015/12/15/encouragement/feed + 0 +
+ + The Ultimate Optimization + http://blog.boochtek.com/2015/12/06/ultimate-optimization + http://blog.boochtek.com/2015/12/06/ultimate-optimization#respond + Mon, 07 Dec 2015 05:28:43 +0000 + + + + http://blog.boochtek.com/?p=277 + Continue reading "The Ultimate Optimization"]]> + I got my first computer in 1984. It was a Commodore 64. I had to do extra chores and save up my allowance to buy it. I was 13.

+

Back then, Sears and other retail stores had Commodore 64 computers out on display. Whenever I went to the store, I’d write a short BASIC program and leave it running on the display computers:

+
10 PRINT "CRAIG IS GREAT!"
+20 GOTO 10
+

Hey, I was a 13-year-old kid. Later, I got a little more sophisticated. I’d change the background color instead. I still remember the POKE address:

+
10 FOR I=0 TO 255
+20 POKE 53281, I
+30 NEXT I
+40 GOTO 10
+RUN
+

This was fast enough to change the background color every few scan lines, creating a flashing scrolling effect.

+

Later, I learned 6502 assembly language. I translated the BASIC program into assembler, and memorized the bytes to type in at the store. In assembly language, the background color would change several times per scan line. The effect was kind of psychedelic.

+

All that happened in the mid-1980s.

+

Fast-forward to about 2000 or so. I was telling the above story after a St. Louis LUG meeting. I explained how I had memorized the 10 or 12 bytes of machine code, and would leave the program running with its psychedelic effect.

+

After thinking about it for a bit, I thought that 10 or 12 bytes seemed too much. It actually bothered me — I couldn’t fall asleep when I got home. I got up and found my old 6502 manuals. I figured out how to write the code in 7 bytes. I installed the Vice C64 emulator on my Linux desktop, and tested my code. It worked as expected. (The emulator was already clock-cycle perfect by then.) Here’s the assembly code:

+
INX         ; $E8           ; 232
+STX $D021   ; $8E $21 $D0   ; 142 33 208   ; $D021 = 53281
+JMP $C000   ; $4C $00 $C0   ; 76 0 192     ; $C000 = 49152
+

Here’s the BASIC program to store that program in memory and run it:

+
10 FOR N=49152 TO 49152+6: READ Q : POKE N, Q : NEXT
+20 DATA 232, 142, 33, 208, 76, 0, 192
+30 SYS 49152
+RUN
+

The moral of the story is that you can optimize even a 10-byte program, 15 years after the last time it was used. So don’t tell me that your program can’t be improved, no matter how small it is.

+

PS. I rewrote the code above while writing this article in 2015, about 15 years after the last time I rewrote it. And I again downloaded Vice to test it, this time on Mac OS X.

+]]>
+ http://blog.boochtek.com/2015/12/06/ultimate-optimization/feed + 0 +
+ + Show and Tell + http://blog.boochtek.com/2015/11/24/show-and-tell + http://blog.boochtek.com/2015/11/24/show-and-tell#respond + Wed, 25 Nov 2015 05:21:06 +0000 + + + + http://blog.boochtek.com/?p=271 + Continue reading "Show and Tell"]]> + I’m wrapping up my current consulting gig at Mercy in December, and starting a new gig at CenturyLink. I’m a software developer, but that’s only a part of what I do. What I really do is join a team and help them improve the way they work — both their processes and their technical skills.

+

I think this is a key differentiator for me as a consultant. Most consultants (and Agile coaches) come in and tell people what to do. I don’t like to just tell people what to do. I’d much prefer to work side-by-side with them, getting a better understanding of what their challenges are. Once I have a better understanding of the challenges, I’m able to better brainstorm some ideas to try. Then we can experiment to see what will work and what won’t.

+

Instead of telling people what to do, I show them how. Most people learn better from seeing than from hearing. They also learn better if you explain how and why, not just what. So showing them how to do something is more effective than telling them. By showing and doing, you can also set a good example. This is especially important when collaboration is a large part of what needs to be improved.

+

I’ve found that this style of consulting is more highly respected by everyone. I build trust with developers by working closely with them. Managers like to keep me around once they see how effective these methods can be, so the gigs I take on tend to last relatively long.

+

The biggest problem I have is explaining how this works. I don’t really know what to put on my résumé. Sometimes I call myself an Agile practitioner, and sometimes an Agile player/coach. But those aren’t terribly satisfying descriptions. I’m considering actually putting “I help teams improve the way they work — both their processes and their technical skills” on the résumé. But that seems awkward, and misses the show versus tell part. I’d be open to any suggestions.

+

 

+]]>
+ http://blog.boochtek.com/2015/11/24/show-and-tell/feed + 0 +
+ + Happiness Retrospective + http://blog.boochtek.com/2015/11/09/happiness-retrospective + http://blog.boochtek.com/2015/11/09/happiness-retrospective#respond + Tue, 10 Nov 2015 05:56:58 +0000 + + + + + http://blog.boochtek.com/?p=268 + Continue reading "Happiness Retrospective"]]> + I facilitated a retrospective today; it was one of the best retros I’ve ever been involved with. I figured out what activities I wanted to do earlier in the morning. They were really quite simple. I wanted to focus on happiness.

+

How happy are you at work?

+

I started with two questions that I’ve used with teams before, to some success (although not so successful for one particular team). The first question I asked was “How happy are you at work?” I had them put a rating from 0 to 10, with 0 meaning they should have quit last week, and 10 meaning they couldn’t imaging being happier at work.

+

The answers were mostly 7s and 8s, with a 5 and a 9. The average came to 7.5, which is pretty good. The 5 concerns me a bit, especially that it’s 2 points lower than anyone else’s answer.

+

How effective do you think the teams is?

+

The next question I asked was “How effective do you think the teams is?”. Again, from 0 to 10, with 0 meaning they can’t accomplish anything, and 10 meaning you couldn’t imagine a more effective team.

+

When I ask both of these questions, the scores are always highly correlated. If your team isn’t doing good work, it’ll make you unhappy. And if you are unhappy, you’re less likely to do your best work. This team was no different; the 5 and 9 became a 6 and a 10, and most of the 7s became 8s, for an average of just under 8.

+

What makes you happy at work?

+

The next question I asked was “What makes you happy at work?”. The answers were mostly about the teamwork and teammates. This went quicker than I expected. At this point, I was worried the retro would only last a little more than 30 minutes.

+

What would make you happier at work?

+

The final question I asked was “What would make you happier at work?”. This was the real pay-off. We spent about half an hour just talking about the things that would make us happier, and what we could do to improve our happiness. We came up with 10 potential action items. I usually limit teams to trying 3 or 4 action items, but most of the items are quite small, so we’re going to try 6 of them. One is just observing another team’s standup meetings, to see how they’re using their time effectively.

+

Everyone went away feeling that this was a really good retro. It felt good to focus on happiness. Happiness is something I’ve been talking a lot about on the This Agile Life podcast, and it felt good to take some action on it. I’ve done “positive-only” retros before, but this one felt even better than that, by specifically targeting happiness and how we can achieve it.

+]]>
+ http://blog.boochtek.com/2015/11/09/happiness-retrospective/feed + 0 +
+
+
diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..58e849e --- /dev/null +++ b/public/index.html @@ -0,0 +1,482 @@ + + + + + + + + + + + + + + + +BoochTek, LLC – Web Development, Ruby on Rails, Open Source + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + +
+
+ +

You Don’t Have to Be Right

+ + + +
+

Not too long ago, I was asked during a job interview “how do you convince your teammates that you’re right?” I answered with probably the most surprising answer: “I don’t.”

+

It’s taken me years to realize that being right isn’t terribly important. Especially when there’s more than one right answer — which there usually is. It’s more important to work as a team. It’s more important to be respected and to have respect for others.

+

A few weeks after that interview, I was pair programming with the person who had asked me the question. We’re both senior developers, so we both have a lot of experience and opinions based on that experience. We were writing a shell script, and we both had an idea in mind about how to write the code. I let him proceed with his idea. It was a pretty good idea; probably better than mine. But we paired very effectively. I’d let him finish some code, and I’d find a way to make it better. Then he’d find a way to make my code better. What we ended up with was so much better than my original idea, and his original idea as well. I commented on this, and he agreed. We left that pairing session feeling the high of having accomplished something rare — two very experience programmers coming up with better code than we could have imagined going in.

+

I like to think of writing code as a path towards a destination. The destination is good code that does what it’s supposed to, is readable (intention revealing), is concise, and is well-factored so as to be easy to change in the future. But there are many paths to that destination. Especially when pair programming, the code gets refined as ideas are shared, getting you closer and closer to that destination. Sometimes the different paths end up with the same code; sometimes they end up with different code. But if the code does its job well (according to those criteria), it doesn’t really matter what code you ended up with.

+

Thinking back to the original question, I think the answer is a little more nuanced. (I’m pretty sure I followed up with an explanation, but I don’t recall the details.) It truly doesn’t matter in a lot of cases. If there are different paths that lead to the same destination, and they’re all roughly equal, then my rule works. It can also work even if the first path you try (a “wrong” path) doesn’t work out, as long as it’s easy enough to try a different path. I find that the majority of day-to-day coding fits these conditions.

+

But there are some places where it’s costly to get things wrong the first time. In these cases, it’s worth spending some time thinking about and discussing the alternatives before getting started. Software architecture is the big one that comes to mind. In general, weighing the pros and cons of each option and applying previous experience works best.

+

So next time you think about trying to convince someone you’re right, fight the urge. Instead, let them show you what they’re thinking. They just might surprise you. And more importantly, you might surprise yourself.

+
+ + +
+ +
+
+ +

My First Open Space

+ + + +
+

I recently attended an Open Space hosted at work. I’d never been to an Open Space, and didn’t know what to expect. We’d been told that this was a workshop to help Engineering Managers (my role), Product Owners, and Product Analysts find better ways of working together. But due to the way an Open Space works, it evolved into something completely different — and better.

+

We’d brought in Diana Larsen to facilitate the Open Space. Diana is a stalwart of the Agile community, focusing on how people and teams interact. She literally (co-)wrote the book on Agile retrospectives. Diana was also kind enough to be our guest at an Agile LINC meetup later in the evening.

+

The morning started off with all the participants sitting in chairs arranged in a circle. Diana rang a chime, a nice soothing tone that literally set the tone for the day. (Most people didn’t seem to like it, but I thought it had its purpose.) It also acted as a call to gather in the meeting space. She then walked around the inside of the circle as she explained what was going to happen.

+

The idea behind Open Spaces came with the realization that at a conference, the “hallway track” (ad hoc discussions in the hallways) was often more valuable than the scheduled talks. So they figured out a way to capture that experience. There are only a few rules:

+
    +
  • Whoever shows up is the right group
  • +
  • Whatever happens is the only thing that could have
  • +
  • Be prepared to be surprised
  • +
  • Whenever it starts is the right time
  • +
  • When it’s over, it’s over
  • +
  • The Law of 2 Feet: If you’re not learning or contributing, go somewhere else
  • +
+

Once Diana set the scene and explained the rules, people came up and presented ideas for topics. If you proposed a topic, you chose a time and location on the topic board. At that time, you’d facilitate a discussion on that topic. The format was really conducive to discussions. There was no preparation, so discussions were from the heart — lots of people felt that they could contribute.

+

Being new to the company (only 2 months), I got a lot out of the workshop, beyond the content itself. I got to get to know several more people. I’d even say I made a few friends. Having open discussions, we found that many of the teams were having the same issues. This made it easy for me to talk to other people.

+

All-in-all, I found the Open Space to be extremely conducive to discussions aimed at identifying issues, brainstorming solutions, and planning action items. I felt like we made great strides in addressing some of the biggest problems our organization is currently facing.

+
+ + +
+ +
+
+ +

Resolutions for 2016

+ + + +
+

I’ve written up a list of career-related resolutions the past few years. I’ve been pretty successful meeting those goals, so I’m going to continue the tradition.

+

Coding

+

I want to increase the Open Source code I write this year. That’s basically everything I write that’s not directly related to work.

+

I took over maintenance of Virtus late last year, but haven’t done a good job at finding the time for that. That’s probably more responding to issues on GitHub and merging pull requests. But eventually, I’ll likely add some features and do some refactoring.

+

My goal is to work on 6 apps and libraries this year. That’s a pretty aggressive goal, but I want to focus on both learning new things and building some simple but useful things. Think MVP — focus on getting a lot of bang for not too much effort.

+

I want to work on a new Rails app, to familiarize myself with Rails 5. I want to get some experience with some more modern gems. I also want to see if I can use some new techniques with a Rails app, especially things that might help us get closer to a hexagonal architecture. I’m also hoping to work with some different web frameworks — maybe Lotus, Rodakase, Trailblazer, or Phoenix. Or maybe one of the Crystal web frameworks.

+

I also want to work on a couple Elm apps. I started the STL Elm group as an excuse to learn the language. I’d like to work on a Twitter client, that might eventually turn into a more general reading app. Beyond Twitter, it would keep track of things I want to read. The other Elm app I’d like to write is a game, based on the old Space Taxi game I loved to play on the Commodore 64.

+

I wrote a micro-ORM last year called Ruby Preserves. I’ve started converting that from working with raw SQL to sitting atop the awesome Sequel gem. The simplicity is still there so far, and basing it on Sequel has so far gone easier than I expected. But I haven’t figured out how to do relationships (JOINs) yet; that will be the real test.

+

Conferences

+

I’m pretty happy with my 2015 conference experience. I want to keep my conference level about the same. I don’t really want to commit to more talks than I gave last year. Conference presentations take a lot of time and energy to write and practice.

+

I’m planning to go to RailsConf, RubyConf, and Strange Loop. I’m hoping to give a talk at each of those, if I can. But I’ll probably attend all of them even if I’m not speaking. I’m also submitting a talk to Agile 2016. I’m unlikely to go to any other conferences, and even more unlikely to give more than 4 conference talks.

+

Writing

+

I really want to finish my Effective Agile book this year. I’m going to try to pull Amos in to pair with me on it occasionally, to move it forward. If I’m able to get it done, I’d also like to maybe write a book on Elm.

+

I actually want to do less blogging this year. Well, sort of. I want to put coding and book writing ahead of blogging. If that means less blogging, I’m okay with that. So my goal for blogging is more like every other week. And when I do write a blog article, I’d rather it be related to some code I’m working on, something that I could use in my book, or some more in-depth thoughts about programming language design.

+

Job Hunting

+

The contract I recently started at CenturyLink Cloud is open-ended, so I’ll likely be there for the entire year. I’m enjoying it so far; it’s a challenge, but I think I can make a significant impact. However, I’m really interested in moving to San Francisco in 2017. So I’m going to work on preparing for that in several ways.

+

I’m going to resurrect my LinkedIn account. I’ve neglected it for a few years now. I’ll make sure everything is up to date, and make some connections I’ve been putting off. I also want to reclaim my Stack Exchange and Hacker News accounts.

+

I’m going to pay a an expert to help me revamp my résumé. I think it’s pretty decent, but it could use some freshening up. I want it to stand out. I want to do a better job of explaining what I really do — which is to join a team to help them improve their processes and their technical skills.

+

I’ll also need to clean up all my web sites, so they look nice when people come to take a look at them. This includes my personal site, consulting business site, wiki, and blogs. I should also upgrade my server to the latest version of Debian, and use Ansible and some other tools to provision it using the principles of Infrastructure as Code.

+
+ + +
+ +
+
+ +

Team Values

+ + + +
+

I held a retrospective with my new team last week. The team includes 2 senior developers, 2 junior developers, a product owner, and a product analyst. I’ve joined the team as an engineering manager, which I think of more as a team lead with an elevated title. Being new to this group, I wanted a way to understand their values. What motivates them? What common values do we share that we can leverage to move forward in the same direction?

+

I started out with a pretty simple question: “What do you value (in regards to what we’re building); what are you willing to fight for?” I asked them each to write down several values and then put them on the board before looking at everyone else’s answers. I also asked each person to rank their values in order of importance.

+

It turned out that my question was a little vague. Some people thought about the question in terms of the end result, and some in terms of the process of creating the software. In some ways that was a bit of a problem, because the different interpretations led people in different directions. But in other ways, not pushing them in any particular direction got more varied answers, exposing how they think about the project and the product.

+

My list (in order) was Effectiveness (doing the right thing), Quality (doing things right the first time), Happiness, Purpose, and Teamwork. In retrospect, I should have put Happiness first. If I’m not happy at work, I don’t really want to be there, and need to move on. (Sometimes I can trade some happiness at work for more happiness at home, but I’m definitely a person that needs to be happy at work.) The other values serve to improve my happiness, but the happiness is more important.

+

My own issue ranking my values turned out to be a problem with the ranking in general. Should they be ranked in importance of the necessity of the value as an outcome, or in importance of the necessity to focus on the value? I think the former is what I was looking for, but even I wasn’t clear on that when I began the exercise.

+

After everyone put their values up on the board, we read them off. Then I asked the team to choose several values that we share as a team. We pulled them off the individual members’ lists, and put them in the team list. Then I asked each person to come up and rank those values, then explain why they had ordered them that way, especially when they ordered them significantly different than the last person. I was hoping to come to some convergence of the rank over time, so we could document our values in rank order. That didn’t happen. But the discussion was illuminating to me and to everyone on the team.

+

I think the most interesting part about the lack of convergence was the difference between the developers and the product guys. The product guys definitely viewed the values more in terms of outcomes than the process. That makes sense — they’re not as intimately involved in the process of building the product.

+

We were able to converge on the top priority though: Will the user buy the product? This was a combination of a couple different values that we merged together. This included the end user experience as well as making sure the team would continue to have a reason for existing. The rest of the values we left unordered: Teamwork (cohesiveness), Data driven decisions, Team ownership, Simplicity, Effectiveness, Maintainability / Supportability, Quality, Automation, and Performance. I think that’s a pretty decent list.

+

As a couple teammates pointed out, those values are probably in part a reflection of this current point in time. If I asked the same question some other time, under different conditions and team dynamics, the answers would probably change a bit. And we’d probably come up with other answers if asked again, just due to randomness of the way we think about these things.

+

But I don’t think I’d do this activity a second time with the team. It was really about understanding our motivations — both our own, and those of our teammates. I found it effective in that way, and also in helping the team to think about our culture and how we can work to shape it to help us all push in the same direction.

+

There are a few caveats. When I asked for feedback on the exercise, one teammate pointed out that it wouldn’t work if people weren’t honest about their values, and they answered with what they thought management or their teammates wanted to hear. I don’t think that was an issue with this group, but it’s something to keep in mind.

+

The other major thing I’d do is to make it clear up front that I’m not looking for any action items from this activity; it’s more about understanding each other and ourselves. And next time, I’ll work to clarify how to rank the values.

+

I would recommend this activity for a new team, or when the makeup of the team is changing in some significant way. I wish there was a way to help a team converge on the ranking of their values, but I suppose I should be happy that agreeing on the set of important values went pretty quickly. And the diversity of ideas and opinions is probably a blessing that I should be embracing — the more ideas we have, the wider the variety of solutions we can imagine.

+
+ + +
+ +
+
+ +

2015 Year in Review

+ + + +
+

It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+
+ + +
+ +
+
+ +

Face Your Fears

+ + + +
+

I’ve always been someone who faces my fears.

+

I have a moderate case of arachnophobia. I don’t run away when I see a spider, but it creeps my out when one is crawling on me. When I was in college, I decided to buy a tarantula to attempt to get over my irrational fear of spiders. I thought I’d be able to get more comfortable with the tarantula over time, eventually to the point of letting it crawl on my arm. It didn’t work. Although I did find that my fear of tarantulas is rational — I got a terrible case of hives just from touching the urticating hairs that fell off into its water sponge.

+

Last week, I was on vacation in Mexico. One of the excursions we took involved jumping in the water a lot. I’m not a strong swimmer — mostly because I have a hard time closing my nose; I hold my nose when I jump in. We zip-lined into the water a lot. At one point, there was a cliff to dive into the water from. It was about 15 feet above the water. It didn’t look so far down before jumping. But it felt like a really long way down the first time I jumped from it. It was pretty scary for me. So I did it a second time. There wasn’t any peer pressure to jump a second time. I literally jumped a second time specifically because I was scared.

+

Fear is a weird thing. Fear is there to protect us. But it’s there to protect us from the dangers of the African savannah. Most of the things our fears protect us from don’t exist in our everyday modern lives. So maybe we should work to gain a better understanding of how our fears work, to figure out when to pay attention to them and when to ignore them.

+

Fortunately, our brain has a good mechanism to help us do this. Our brains basically have 2 main processing systems. The first one is for quick reactions. This one involves things that are nearly reflexes. Fear is in this system. The second system is our analytical reasoning system. This system takes longer to process, but is able to take on more information.

+

Whenever the situation allows us time for both systems to work, we need to listen to them both. We need to listen to our fears, because they’re there for a reason. But that reason might not pertain to our situation. So we need to realize that, and let the slow analytical system determine if we should ignore our fears.

+

If we don’t allow both systems to work, we’re not taking full advantage of our brains; we’re not taking full advantage of the situations that life is presenting to us.

+
+ + +
+ +
+
+ +

Encouragement

+ + + +
+

I’ve been on vacation the past week, in Cozumel, Mexico. One day, we went on an excursion called Xenotes. A cenote (say-NO-tay) is a sinkhole filled with fresh water. (The “X” is to give it a more Mayan-sounding trademarkable name.) We had a lot of fun swimming, kayaking, and zip-lining. A bilingual tour guide led our group, which consisted of people from across the US and South America, of various ages and physical abilities.

+

Our tour guide was a lot of fun. He made jokes, told us about the cenotes, and led us in the activities. But he also encouraged us. When someone was scared to do something, he was supportive. He told us that it was okay, and we could do it. But we also felt like it was OK to fail, if we really couldn’t. It really felt like his encouragement was literally creating courage.

+

What was really neat was that despite the language barrier, everyone else was also supportive and encouraging. Everyone cheered with encouragement before someone would attempt something difficult. And we’d cheer especially loud once someone accomplished something that was difficult for their abilities.

+

It was an awesome feeling to feel so supported. It made me feel like I was in a safe place, where I could try new things that were a little past my comfort zone. I was able to do the zip-line upside-down. I jumped off a 15-foot cliff into the water. I even jumped off the cliff a second time, even though I was a little scared.

+

Back at the resort, we played some volleyball in the pool. It was a similar situation, with players of varying ages and abilities. Again, we tried to help the weaker players feel comfortable so they could improve without feeling judged or self-conscious. It helped everyone have a good time. It made everything more fun to be in such a supportive environment, whether I was in a position as one of the more skilled (volleyball), or one of the less skilled (jumping or zip-lining into the water). We were all able to accomplish more, with less effort.

+

These experiences provide a good lesson that can be applied in a lot of places. Such a supportive environment would help any relationship, and any team. I’ve been on a couple really good software development teams, but I don’t think any of them have been as supportive as these two groups of strangers.

+

I’ve decided that this should be one of my goals as a team leader. I want to create an encouraging environment for the whole team. I want to make sure that everyone is comfortable enough that they feel like they can try things that are difficult, even if they might fail (as long as nobody gets hurt).

+

If a group of strangers can do this, so can your team. So can you and your significant others. We need to work every day to make sure that we’re supporting each other. It’s the best way to get everyone to achieve more.

+
+ + +
+ +
+
+ +

The Ultimate Optimization

+ + + +
+

I got my first computer in 1984. It was a Commodore 64. I had to do extra chores and save up my allowance to buy it. I was 13.

+

Back then, Sears and other retail stores had Commodore 64 computers out on display. Whenever I went to the store, I’d write a short BASIC program and leave it running on the display computers:

+
10 PRINT "CRAIG IS GREAT!"
+20 GOTO 10
+

Hey, I was a 13-year-old kid. Later, I got a little more sophisticated. I’d change the background color instead. I still remember the POKE address:

+
10 FOR I=0 TO 255
+20 POKE 53281, I
+30 NEXT I
+40 GOTO 10
+RUN
+

This was fast enough to change the background color every few scan lines, creating a flashing scrolling effect.

+

Later, I learned 6502 assembly language. I translated the BASIC program into assembler, and memorized the bytes to type in at the store. In assembly language, the background color would change several times per scan line. The effect was kind of psychedelic.

+

All that happened in the mid-1980s.

+

Fast-forward to about 2000 or so. I was telling the above story after a St. Louis LUG meeting. I explained how I had memorized the 10 or 12 bytes of machine code, and would leave the program running with its psychedelic effect.

+

After thinking about it for a bit, I thought that 10 or 12 bytes seemed too much. It actually bothered me — I couldn’t fall asleep when I got home. I got up and found my old 6502 manuals. I figured out how to write the code in 7 bytes. I installed the Vice C64 emulator on my Linux desktop, and tested my code. It worked as expected. (The emulator was already clock-cycle perfect by then.) Here’s the assembly code:

+
INX         ; $E8           ; 232
+STX $D021   ; $8E $21 $D0   ; 142 33 208   ; $D021 = 53281
+JMP $C000   ; $4C $00 $C0   ; 76 0 192     ; $C000 = 49152
+

Here’s the BASIC program to store that program in memory and run it:

+
10 FOR N=49152 TO 49152+6: READ Q : POKE N, Q : NEXT
+20 DATA 232, 142, 33, 208, 76, 0, 192
+30 SYS 49152
+RUN
+

The moral of the story is that you can optimize even a 10-byte program, 15 years after the last time it was used. So don’t tell me that your program can’t be improved, no matter how small it is.

+

PS. I rewrote the code above while writing this article in 2015, about 15 years after the last time I rewrote it. And I again downloaded Vice to test it, this time on Mac OS X.

+
+ + +
+ +
+
+ +

Show and Tell

+ + + +
+

I’m wrapping up my current consulting gig at Mercy in December, and starting a new gig at CenturyLink. I’m a software developer, but that’s only a part of what I do. What I really do is join a team and help them improve the way they work — both their processes and their technical skills.

+

I think this is a key differentiator for me as a consultant. Most consultants (and Agile coaches) come in and tell people what to do. I don’t like to just tell people what to do. I’d much prefer to work side-by-side with them, getting a better understanding of what their challenges are. Once I have a better understanding of the challenges, I’m able to better brainstorm some ideas to try. Then we can experiment to see what will work and what won’t.

+

Instead of telling people what to do, I show them how. Most people learn better from seeing than from hearing. They also learn better if you explain how and why, not just what. So showing them how to do something is more effective than telling them. By showing and doing, you can also set a good example. This is especially important when collaboration is a large part of what needs to be improved.

+

I’ve found that this style of consulting is more highly respected by everyone. I build trust with developers by working closely with them. Managers like to keep me around once they see how effective these methods can be, so the gigs I take on tend to last relatively long.

+

The biggest problem I have is explaining how this works. I don’t really know what to put on my résumé. Sometimes I call myself an Agile practitioner, and sometimes an Agile player/coach. But those aren’t terribly satisfying descriptions. I’m considering actually putting “I help teams improve the way they work — both their processes and their technical skills” on the résumé. But that seems awkward, and misses the show versus tell part. I’d be open to any suggestions.

+

 

+
+ + +
+ +
+
+ +

Happiness Retrospective

+ + + +
+

I facilitated a retrospective today; it was one of the best retros I’ve ever been involved with. I figured out what activities I wanted to do earlier in the morning. They were really quite simple. I wanted to focus on happiness.

+

How happy are you at work?

+

I started with two questions that I’ve used with teams before, to some success (although not so successful for one particular team). The first question I asked was “How happy are you at work?” I had them put a rating from 0 to 10, with 0 meaning they should have quit last week, and 10 meaning they couldn’t imaging being happier at work.

+

The answers were mostly 7s and 8s, with a 5 and a 9. The average came to 7.5, which is pretty good. The 5 concerns me a bit, especially that it’s 2 points lower than anyone else’s answer.

+

How effective do you think the teams is?

+

The next question I asked was “How effective do you think the teams is?”. Again, from 0 to 10, with 0 meaning they can’t accomplish anything, and 10 meaning you couldn’t imagine a more effective team.

+

When I ask both of these questions, the scores are always highly correlated. If your team isn’t doing good work, it’ll make you unhappy. And if you are unhappy, you’re less likely to do your best work. This team was no different; the 5 and 9 became a 6 and a 10, and most of the 7s became 8s, for an average of just under 8.

+

What makes you happy at work?

+

The next question I asked was “What makes you happy at work?”. The answers were mostly about the teamwork and teammates. This went quicker than I expected. At this point, I was worried the retro would only last a little more than 30 minutes.

+

What would make you happier at work?

+

The final question I asked was “What would make you happier at work?”. This was the real pay-off. We spent about half an hour just talking about the things that would make us happier, and what we could do to improve our happiness. We came up with 10 potential action items. I usually limit teams to trying 3 or 4 action items, but most of the items are quite small, so we’re going to try 6 of them. One is just observing another team’s standup meetings, to see how they’re using their time effectively.

+

Everyone went away feeling that this was a really good retro. It felt good to focus on happiness. Happiness is something I’ve been talking a lot about on the This Agile Life podcast, and it felt good to take some action on it. I’ve done “positive-only” retros before, but this one felt even better than that, by specifically targeting happiness and how we can achieve it.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=124.html b/public/index.html?p=124.html new file mode 100644 index 0000000..50e4224 --- /dev/null +++ b/public/index.html?p=124.html @@ -0,0 +1,412 @@ + + + + + + + + + + + + + + + + +Chording Keyers – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Chording Keyers

+ + + +
+

I’m considering buying a chording keyer. A keyer is a 1-handed text input device. Chording means that you can hit more than 1 key at a time. I’ve been interested in these for a long time actually. They’ve been popular in the wearable computing (“cyborg”) field, but never caught on anywhere else. I’ve always thought that 1-handed chording keyer would be a great input device to use with a cell phone. It’d be even better with a heads-up display like the Google Glass.

+

There are quite a few chording keyers available. It doesn’t appear that any will meet exactly what I’m really looking for, but I might take the leap anyway. It should be a hand-held device. I really want a Bluetooth device, so I can use it with a computer, phone, or tablet. Below are the ones I’m considering.

+

Twiddler

+

The Twiddler has been around quite a while, as probably the main commercially available keyer for wearable computing. There was a time that it was unavailable, but there’s a new version available from HandyKey now. It’s $200 ($220 Canadian), and comes in only USB.

+

The Twiddler looks like a small TV remote. The current version (2.1) has 4 thumb keys, a thumb joystick (for mouse), and 12 small finger keys. It has good community support, including an alternate chord layout.

+

In10did Decatxt

+

In10did has been trying to bring out a few variations of their devices. The Decatxt looks like a pack of cigarettes, with 8 keys on the front and 2 thumb keys. It can be used with 1 or 2 hands. All letters can be typed with 1 finger or 1 thumb and 1 finger.

+

The Decatxt has USB and Bluetooth models, but I’m unable to find pricing or a way to buy one. It looks like there have been no updates for over a year, so I’m concerned that this is going to be vaporware.

+

CyKey

+

The CyKey is based on the Microwriter, which goes back to the early 1980s. The Microwriter was a desktop-based device, but it looks like the CyKey might be able to be used as a hand-held device. It has 9 buttons on the face, arranged in 3 groups of 3 buttons.

+

The CyKey is $125 (£75) and works over InfraRed connected to USB. A Bluetooth model is supposedly in the works.

+

EkaPad

+

The EkaPad is like a cross between the Decatxt and the Twiddler. It has 12 buttons on the face, but no thumb keys. It’s got a nice well-thought-out design that looks quite ergonomic.

+

The EkaPad is $150, available in USB only. A desktop holder is available to hold it upright, or it can be used hand-held.

+

FrogPad

+

The FrogPad is meant to be used on a flat surface only. It looks kind of like a third of a laptop keyboard. It’s got 15 regular keys and 5 modifier keys. Only 1 key and 1 modifier can be chorded. Right-handed and left-handed models are available, with USB connectivity.

+

The FrogPad2 was recently announced, replacing the original FrogPad. It switches to standard desktop-style keys, and adds some more keys along the top. It supports USB On-the-Go and Bluetooth. I’m not really interested in this model with its regular keys, but it doesn’t appear that the original version is being made any longer. The new models are priced at $200. I believe the old models were $150.

+

Chordite

+

The Chordite looks the most promising, but it’s only a concept with some DIY prototypes. It’s a hand-held device, with 2 buttons on each of the 4 fingers — one activated by the distal phalanx and the other by the middle phalanx. This initially sounds like it would be difficult to press 2 different buttons with 1 finger, but I think the way they’re positioned would make it actually work really well.

+

Septambic Keyer

+

The septambic keyer is another DIY concept, hand-built and used by wearable computing folks. It’s fitted to your palm, and you squeeze your fingers toward a fist to activate the buttons. There’s one button for each of the 4 fingers, and 3 for the thumb.

+

Clove 2

+

The Clove 2 is a Bluetooth-enabled glove with 5 buttons — 1 for each finger. It’s another do-it-yourself project.

+

Software Options

+

I found an interesting project called ASETNIOP that allows chording on a standard Qwerty keyboard. (Assuming the keyboard supports multi-key roll-over well enough. Most keyboards should work.) It’s got a really nice online interactive tutorial. You basically chord with the 8 home-row keys, plus the spacebar.

+

For Android, a program called GKOS for Android uses multi-touch with 1 or 2 button presses. The buttons are different sizes, depending on their frequency. I like this concept, but I don’t think the implementation is quite ready for production.

+

I also found an app for iOS called Nintype that is a combination between Swype and a chording keyer.

+

Conclusion

+

I think I’m going to try the Twiddler. If I find the concept to be a good one, hopefully I’ll build myself a Chordite (or have someone build one for me).

+

I’m also going to play with implementing some of the ideas from ASETNIOP, using KeyRemap4MacBook.

+
+ + +
+ +
+ +

+ 4 thoughts on “Chording Keyers”

+ + +
    +
  1. + +
  2. +
  3. +
    + + +
    +

    The In10did DecaTxt is available on eBay and Amazon now. I ordered one recently and had it shipped over from the USA, which was a bit expensive when customs fees are added. It arrived yesterday morning and I plugged it in after arriving home from work. Sadly, it appears to do nothing whatsoever. Not sure if this model is DOA (but surely they test them?), or what. If I can get it to work, or get a replacement shipped, I’ll try to record my WPM with the device every day for a couple of weeks, to see where the improvement curve starts to taper off.

    +
    + +
    +
      +
    1. +
      + + +
      +

      Update: It works just fine! Actually the problem was that my laptop only supports Bluetooth 3.0, but at least 4.0 is needed. It also wasn’t discoverable by my phone because Android 4.3 or newer is needed. Tested it on a newer phone and on my work PC and it connected straight away. Looking forward to trying it on an Android tablet and measuring my speed over a few weeks.

      +
      + +
      +
        +
      1. +
        + + +
        +

        I’m glad you were able to get the DecaTxt connected and working. We are planning for a second version with micro USB rather than mini and a few software changes like being able to shut off the Bluetooth antenna. I’d love to get your feedback if you would email me and offer suggestions.
        +Thanks,
        +Wayne

        +
        + +
        +
      2. +
      +
    2. +
    +
  4. +
+ + + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=184.html b/public/index.html?p=184.html new file mode 100644 index 0000000..37b6826 --- /dev/null +++ b/public/index.html?p=184.html @@ -0,0 +1,266 @@ + + + + + + + + + + + + + + + + + + +Ruby Pattern: Parameterized Module Inclusion | BoochTek, LLC + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+
+ + + + + +
+
+

Ruby Pattern: Parameterized Module Inclusion

+ + +
+ +
+

I’ve run across a pattern in Ruby lately that I really like. It solves some problems that I’ve struggled with for several years. Let me start with the problems.

+

Let’s say you want to include an ORM in a model class, and want to tell it what database table to use. Typically, you’d do this:

+
class User
+  include MyORM::Model
+  table 'people'
+end
+

But that table call is more like an option to the module inclusion than anything else. So what we’d really like is something like this:

+
class User
+  include MyORM::Model, table: 'people'
+end
+

But that’s not valid Ruby; include doesn’t let you pass anything other than a module.

+

So when I was learning about Virtus, I noticed that its example of how to include it is a bit different than the standard Ruby idiomatic include:

+
class User
+  include Virtus.model
+end
+

At first glance, it reads like the first example. But on closer inspection and consideration, it’s quite a bit different. Where MyORM::Model is a constant that refers to a module, Virtus.model is a method call. So there’s a method named model in the Virtus module. That method returns another module — which is exactly what’s needed in order to include it into our model class.

+

The easiest way to implement Virtus.model would be this:

+
module Virtus
+  def model
+    ::Virtus::Model
+  end
+end
+
+module Virtus::Model
+  # ...
+end
+

If Virtus.model doesn’t need to take any arguments, that’s perfectly fine. In fact, I’ve started to use this implementation of the pattern for modules that don’t need parameters.

+

Because Virtus.model is a method, we can also call it with options:

+
class User
+  include Virtus.model(constructor: false, mass_assignment: false)
+end
+

We could even pass a block. But how do we process those options? There are a few different ways. However we do it, we have to be sure to return a module. And we can create modules in a few different ways.

+

Virtus uses the builder pattern. It takes the parameters passed in and builds a module dynamically. By that, I mean that it calls Module.new and then adds methods to that module. It does this by mixing in other modules, but it could do it by dynamically defining methods as well.

+

I’ve never seen this pattern in any other language. It’s obviously only possible because we can dynamically create modules.

+

The use of this idiom seems to be catching on a bit in the Ruby community. I’ve started using it myself, and will be adding it to my Includable::ActiveRecord gem soon.

+

 

+

 

+
+ + +
+ +
+ + +

+ 2 thoughts on “Ruby Pattern: Parameterized Module Inclusion

+ + +
    +
  1. +
    + + +

    I think it would be interesting to try using a block as the config to your include. Make a capitalized method that returns a module. Then you could pass a block with curly braces to that method.

    +

    +

    class User
    +  include MyORM::Model {
    +    table "people"
    +  }
    +end
    +

    +
    + + +
    + +
  2. +
  3. +
    + + +

    I seem to have forgotten to mention that this is related to the Factory pattern. In fact, I think it’s a special case of the Factory pattern.

    +

    I thought I was using this pattern on the following code, but realized that it’s just the Factory pattern:

    +

    +

    +module Preserves
    +  def self.repository(*options, &block)
    +    repository = Repository.new(*options)
    +    repository.instance_eval(&block)
    +    repository
    +  end
    +end
    +
    +UserRepository = Preserves.repository(model: User) do
    +end
    +
    +

    +
    + + +
    + +
  4. +
+ + + + +
+

Leave a Reply

+
+

Your email address will not be published. Required fields are marked *

+ +

+

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

+ + + +

+

+
+ +
+ + +
+
+ + +
+ + +
+ + + + + + \ No newline at end of file diff --git a/public/index.html?p=198.html b/public/index.html?p=198.html new file mode 100644 index 0000000..4c2e7da --- /dev/null +++ b/public/index.html?p=198.html @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + +Resolutions – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Resolutions

+ + + +
+

January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=2.html b/public/index.html?p=2.html new file mode 100644 index 0000000..573684f --- /dev/null +++ b/public/index.html?p=2.html @@ -0,0 +1,252 @@ + + + + + + + + + + + + + + + +About – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

About

+ + +
+

BoochTek, LLC is a small web development company, based in St. Louis, Missouri. We specialize in Ruby, Ruby on Rails, Rails rescue projects, Agile methodologies, JavaScript, and HTML 5. We also do GNU/Linux system administration and network security.

+
+ + +
+ +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=215.html b/public/index.html?p=215.html new file mode 100644 index 0000000..5938ee1 --- /dev/null +++ b/public/index.html?p=215.html @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + +Good Enough | BoochTek, LLC + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+
+ + + + + +
+
+

Good Enough

+ + +
+ +
+

I ran into some former colleagues recently, from a company where I had worked to help transform the team to be more Agile. They’ve gone through some reorganization and management changes recently. One of the guys said that their team culture has helped them maintain quality in the face of those changes. This struck me as odd, since I had considered the work I had done there as somewhat of a disappointment. While I felt I had made a small dent, I didn’t feel like I’d made a true Agile transformation. Much of what I had taught didn’t seem to “stick”.

+

Later that night I thought about why my opinion of the team and his opinion were so different. There are a lot of reasons why an Agile “transformation” could fail. It could be due to lack of support from management. Or even worse, not getting the team members to buy into the ideas — usually due to fear of change. But while those had some effect in this case, they weren’t really the main issues. Now that I’ve had some time and some distance from that team, I’ve gained some clarity on what the real issues were.

+

I think the reason that I think this transformation was a failure was due to the continued pace of change that true Agile requires. Basically the team experienced “change fatigue”, where everything keeps changing and you feel like you can never catch your breath. But the more interesting thing is how our perceptions differed. He seemed to think that the transformation was a success — they’re doing things better than they used to. I view it as more of a failure, because they basically stopped improving — at least at the pace that I was hoping for.

+

I think this difference in mindset is pretty fundamental. My view of Agile involves continuous improvement — always inspecting and adapting our processes. I suppose that means that my preferred flavor of Agile is “Kaizen“. But other people don’t see improvement and change as a constant — they see each change as a means to an end. I’m starting to realize that neither viewpoint is objectively correct. Maybe they’re both fine viewpoints to have. Maybe I shouldn’t be so hard on myself and view that engagement as a failure, especially if my teammates view it as a success. Maybe perfection is the enemy of the good. And maybe I need to learn to be happy with “good enough”.

+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+
+

Your email address will not be published. Required fields are marked *

+ +

+

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

+ + + +

+

+
+ +
+ + +
+
+ + +
+ + +
+ + + + + + \ No newline at end of file diff --git a/public/index.html?p=225.html b/public/index.html?p=225.html new file mode 100644 index 0000000..94d5fb3 --- /dev/null +++ b/public/index.html?p=225.html @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + +When Should We Do TDD? | BoochTek, LLC + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+
+ + + + + +
+
+

When Should We Do TDD?

+ + +
+ +
+

On a recent episode (78) of This Agile Life, my fellow hosts talked about when to do Test-Driven Development (TDD). They all said that you should always do TDD — at least for anything that will go into production; there’s an exception for experimenting or “spiking”.

+

I wasn’t on that episode, but later commented on the topic. (Episode 83 — which wasn’t really all that great.) My take was slightly different. I said that you should do TDD only when the benefits outweigh the costs. Unfortunately, we usually greatly underestimate the benefits. And the costs often seem high at first, because it takes some time to get good at writing automated tests. Not to mention that both the costs and the benefits are usually hard to measure.

+

What is the cost of writing automated tests? I’ve asked this question before and recorded the answers (inasmuch as we have them) in a previous blog entry. TDD costs us about 10-30% in short-term productivity. The benefit that they found was a reduction in bugs by 30-90%, and a decrease in code complexity by about 30%.

+

But what about when the costs are higher than the normal 10 to 30 percent? One good example of this is when there’s no test framework for the situation you’re testing. This might be a new language or framework that you’re working with. More likely it’s a complex API that you have to mock out. So that could increase the cost of automated testing so as to outweigh the benefits — especially on a short project. I could imagine situations where the cost of writing the mocks could eat up more than the project itself.

+

Another case where we might consider skipping testing is when we’re more concerned about time to market than quality. This is almost always a mistake. Your code will almost always last longer than you expect. (Remember Y2K?) And if the code lasts longer than you expect, that means you’ll have to deal with bugs that whole time. But we have to work with the information we have at the time we make our decisions. And sometimes that might tell us that time to market is more important than anything.

+

One final case I can imagine is when a true expert is coding something that they’re very familiar with. I could picture someone like Uncle Bob writing code (in a language that he’s familiar with) without tests just as effectively as I could write code with tests.

+

But these situations should not be common; they’re edge cases. In almost all real-world cases, TDD is the right thing to do. Don’t forget, TDD is also a design discipline — it helps you design a better API. So keep doing TDD. But as with any practice, don’t do it blindly without considering why we’re doing it. Make sure you understand the costs, and whether the benefits outweigh the costs.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+
+

Your email address will not be published. Required fields are marked *

+ +

+

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

+ + + +

+

+
+ +
+ + +
+
+ + +
+ + +
+ + + + + + \ No newline at end of file diff --git a/public/index.html?p=261.html b/public/index.html?p=261.html new file mode 100644 index 0000000..3a746e0 --- /dev/null +++ b/public/index.html?p=261.html @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + +The Problem With Estimates | BoochTek, LLC + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+
+ + + + + +
+
+

The Problem With Estimates

+ + +
+ +
+
+
+

I’m a big proponent of Agile (mostly XP; I’m mostly anti-Scrum) and I’ve contributed some to the #noestimates “movement”.

+

I don’t really mean that nobody should ever estimate anything. I mean that I’ve never seen useful (fine-grained) estimates anywhere. Here are some of the problems with estimates that I’ve seen frequently:

+
    +
  1. We’re not good at estimating how long things will take. We’re usually optimistic about how quickly we can get things done, and almost always miss thinking about things that will take more time. I’ve never seen a case where a project is completed more quickly than estimated. I’ve only rarely seen fine-grained (story-level) tasks completed more quickly than estimated.
  2. +
  3. Management asks for estimates and then treats them as deadlines. The team then learns to inflate their estimates. Then management learns to reduce the estimates they’re given. Given fudge factors in each direction, the estimate no longer has much reliability. Even if you’re using story points, the point inflation/deflation leads to less consistency and therefore reduced reliability.
  4. +
  5. Estimates that are given are negotiated down, or simply reduced. This leads to the question why you’d ask for an estimate and not take the answer provided. If you’re not going to listen to the answer, why are you asking the question? This is probably the craziest one on the list — given my first point, increasing an estimate would make sense. Reducing the estimates is just magical wishful thinking.
  6. +
  7. Plans change and work is added, but the deadline (presumably based on the estimates) is not changed to correspond with the extra work involved. So again, you’re not actually even using the estimates that were given.
  8. +
  9. Management dictates deadlines arbitrarily, without speaking to the people who will be doing the work. Spending time estimating how long each task will take when the deadline is already set is completely pointless.
  10. +
  11. Almost every deadline is complete bullshit, based on nothing. Often the excuse is that marketing needs to know when something will come out, so that they can let people know about it. Why they need to know the exact release date way in advance, I’ve never been able to figure out. Many people intuitively know that the deadlines are bullshit, and will likely be allowed to slip. The only exception to bullshit deadlines I’ve come across are regulatory deadlines.
  12. +
  13. Estimation at a fine-grained level isn’t necessary. Many Agile teams estimate using story points, and determine a conversion from story points to time based on previous empirical data. This is fine, except that the time spent estimating the story is wasted time — counting the number of stories almost always gives the same predictive power. Teams tend to get better at breaking up stories over time, so that they’re more consistent in size, so this becomes more likely over time.
  14. +
  15. The ultimate purpose of an estimate is to evaluate whether the proposed work will be profitable, and therefore worth doing. Or to compare the ROI (return on investment) between alternative projects. But to know that, you’ll have to know what value that work will provide. I don’t believe I’ve ever seen that done — at least not at a fine-grained level. Usually by the time you’re asked to estimate, the project has already gotten approval to proceed.
  16. +
+

I’ll note that most of these pit management against the team, instead of working together toward a common cause. Most of the practices also lead to seriously demoralizing the team. And most of the time, the estimates aren’t really even taken into account very much.

+

My advice is to first understand the value of a project before you consider estimating the costs. The estimation at this point will be very rough, so make sure that you have a very wide margin between the expected value and the rough estimate of the cost. If you’re pretty certain of the expected value, I’d probably want to make sure I could still be profitable even if it took 3 or 4 times as long to complete as the rough estimate. And if there’s uncertainty in the expected value, much more.

+

Another way to mitigate the risk of throwing money at something that’s not going to have positive ROI is to reduce the feedback loop. Order the work so that the tasks are ranked in order of value. (Realistically, you’ll have dependencies of tasks to worry about, and should consider effort involved too.) So work on the most valuable feature first — get that out into production as soon as possible. Once that’s done, you can assess if your ROI is positive or not. Keep iterating in this fashion, working on the features that will provide the most value first. Keep assessing your ROI, and stop when the ROI is no longer worth it, compared to other projects the team could be working on.

+

At a fine-grained level, if you’re using story points, I’d ask you to do the math to see if just counting the stories would be as effective at predicting how much will be done over time as using the story points. If so, you can save the time the team spends on estimating stories. I’d still recommend spending time talking about stories so that everyone has a shared understanding of what needs to be done, and to break stories up into a smaller, more manageable size — with one acceptance criteria per story. Also take a look to see if empirical average cycle time (how long it takes a single story to move from start to finish) might provide you the predictive power just as well as estimates. (I.e. is it bandwidth or latency that really provides the predictive power you’re looking for?)

+

And don’t forget Hofstadter’s Law: It always takes longer than you expect, even when you take into account Hofstadter’s Law.

+
+
+
+
+
+
+ + +
+ +
+ + + +
+

Leave a Reply

+
+

Your email address will not be published. Required fields are marked *

+ +

+

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

+ + + +

+

+
+ +
+ + +
+
+ + +
+ + +
+ + + + + + \ No newline at end of file diff --git a/public/index.html?p=265.html b/public/index.html?p=265.html new file mode 100644 index 0000000..65b7307 --- /dev/null +++ b/public/index.html?p=265.html @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + +Impromptu Retrospective – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Impromptu Retrospective

+ + + +
+

I’m surprised that I haven’t gotten this story down in print before. It’s something I’ve mentioned many times — including a few times on the podcast. It’s a great story about the power of retrospectives, and it’s a great story about the power of a blameless post-mortem.

+

I don’t recall all the specifics at this point. It was about 5 years ago. I’d just noticed that Arun had made some sort of mistake. That’s fine, people make mistakes. The thing that was different about his mistake was that I had made the same mistake about a week prior. And Amos had made the same mistake about a week before that.

+

Noticing a pattern of mistakes, Amos and I called an impromptu retrospective. We gathered all the developers into a conference room. We explained the problem that we were running into. At first, Arun was defensive. That’s understandable; he thought we were there to come down on him, to lay blame. But we made it clear that we weren’t focusing on him. We admitted that we had also made the same mistake recently. We weren’t there to lay blame; we were there to figure out how our team could stop making the mistake. It took Arun a few minutes to get over the defensiveness.

+

With the defensiveness out of the way, we could focus on the issue at hand. We were able to figure out the root cause of us all making the mistake. (I don’t know if we played the “5 whys” game, but I’m sure we effectively did something similar.) And with that, we were able to change our process, so that nobody else would make the same mistake again.

+

There are 2 important points to this story. First, you don’t have to wait until a scheduled retrospective to hold a retrospective. This one was impromptu, and it’s the best one we ever had. We saw a problem, addressed it, and found a solution in less than an hour. Had we waited until the end of the week, we would have forgotten some of the details, and wouldn’t have been as effective at solving the problem. Second, when addressing problems, take your ego out of the equation. If you’re in a position of authority, take the blame — but never place blame. Focus on what’s important — solving the problem.

+

And don’t forget the Retrospective Prime Directive:

+

Regardless of what we discover, we understand and truly believe that everyone did the best job they could, given what they knew at the time, their skills and abilities, the resources available, and the situation at hand.

+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=268.html b/public/index.html?p=268.html new file mode 100644 index 0000000..eee1d94 --- /dev/null +++ b/public/index.html?p=268.html @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + +Happiness Retrospective – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Happiness Retrospective

+ + + +
+

I facilitated a retrospective today; it was one of the best retros I’ve ever been involved with. I figured out what activities I wanted to do earlier in the morning. They were really quite simple. I wanted to focus on happiness.

+

How happy are you at work?

+

I started with two questions that I’ve used with teams before, to some success (although not so successful for one particular team). The first question I asked was “How happy are you at work?” I had them put a rating from 0 to 10, with 0 meaning they should have quit last week, and 10 meaning they couldn’t imaging being happier at work.

+

The answers were mostly 7s and 8s, with a 5 and a 9. The average came to 7.5, which is pretty good. The 5 concerns me a bit, especially that it’s 2 points lower than anyone else’s answer.

+

How effective do you think the teams is?

+

The next question I asked was “How effective do you think the teams is?”. Again, from 0 to 10, with 0 meaning they can’t accomplish anything, and 10 meaning you couldn’t imagine a more effective team.

+

When I ask both of these questions, the scores are always highly correlated. If your team isn’t doing good work, it’ll make you unhappy. And if you are unhappy, you’re less likely to do your best work. This team was no different; the 5 and 9 became a 6 and a 10, and most of the 7s became 8s, for an average of just under 8.

+

What makes you happy at work?

+

The next question I asked was “What makes you happy at work?”. The answers were mostly about the teamwork and teammates. This went quicker than I expected. At this point, I was worried the retro would only last a little more than 30 minutes.

+

What would make you happier at work?

+

The final question I asked was “What would make you happier at work?”. This was the real pay-off. We spent about half an hour just talking about the things that would make us happier, and what we could do to improve our happiness. We came up with 10 potential action items. I usually limit teams to trying 3 or 4 action items, but most of the items are quite small, so we’re going to try 6 of them. One is just observing another team’s standup meetings, to see how they’re using their time effectively.

+

Everyone went away feeling that this was a really good retro. It felt good to focus on happiness. Happiness is something I’ve been talking a lot about on the This Agile Life podcast, and it felt good to take some action on it. I’ve done “positive-only” retros before, but this one felt even better than that, by specifically targeting happiness and how we can achieve it.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=271.html b/public/index.html?p=271.html new file mode 100644 index 0000000..9e18802 --- /dev/null +++ b/public/index.html?p=271.html @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + +Show and Tell – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Show and Tell

+ + + +
+

I’m wrapping up my current consulting gig at Mercy in December, and starting a new gig at CenturyLink. I’m a software developer, but that’s only a part of what I do. What I really do is join a team and help them improve the way they work — both their processes and their technical skills.

+

I think this is a key differentiator for me as a consultant. Most consultants (and Agile coaches) come in and tell people what to do. I don’t like to just tell people what to do. I’d much prefer to work side-by-side with them, getting a better understanding of what their challenges are. Once I have a better understanding of the challenges, I’m able to better brainstorm some ideas to try. Then we can experiment to see what will work and what won’t.

+

Instead of telling people what to do, I show them how. Most people learn better from seeing than from hearing. They also learn better if you explain how and why, not just what. So showing them how to do something is more effective than telling them. By showing and doing, you can also set a good example. This is especially important when collaboration is a large part of what needs to be improved.

+

I’ve found that this style of consulting is more highly respected by everyone. I build trust with developers by working closely with them. Managers like to keep me around once they see how effective these methods can be, so the gigs I take on tend to last relatively long.

+

The biggest problem I have is explaining how this works. I don’t really know what to put on my résumé. Sometimes I call myself an Agile practitioner, and sometimes an Agile player/coach. But those aren’t terribly satisfying descriptions. I’m considering actually putting “I help teams improve the way they work — both their processes and their technical skills” on the résumé. But that seems awkward, and misses the show versus tell part. I’d be open to any suggestions.

+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=277.html b/public/index.html?p=277.html new file mode 100644 index 0000000..9c4dc99 --- /dev/null +++ b/public/index.html?p=277.html @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + +The Ultimate Optimization – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

The Ultimate Optimization

+ + + +
+

I got my first computer in 1984. It was a Commodore 64. I had to do extra chores and save up my allowance to buy it. I was 13.

+

Back then, Sears and other retail stores had Commodore 64 computers out on display. Whenever I went to the store, I’d write a short BASIC program and leave it running on the display computers:

+
10 PRINT "CRAIG IS GREAT!"
+20 GOTO 10
+

Hey, I was a 13-year-old kid. Later, I got a little more sophisticated. I’d change the background color instead. I still remember the POKE address:

+
10 FOR I=0 TO 255
+20 POKE 53281, I
+30 NEXT I
+40 GOTO 10
+RUN
+

This was fast enough to change the background color every few scan lines, creating a flashing scrolling effect.

+

Later, I learned 6502 assembly language. I translated the BASIC program into assembler, and memorized the bytes to type in at the store. In assembly language, the background color would change several times per scan line. The effect was kind of psychedelic.

+

All that happened in the mid-1980s.

+

Fast-forward to about 2000 or so. I was telling the above story after a St. Louis LUG meeting. I explained how I had memorized the 10 or 12 bytes of machine code, and would leave the program running with its psychedelic effect.

+

After thinking about it for a bit, I thought that 10 or 12 bytes seemed too much. It actually bothered me — I couldn’t fall asleep when I got home. I got up and found my old 6502 manuals. I figured out how to write the code in 7 bytes. I installed the Vice C64 emulator on my Linux desktop, and tested my code. It worked as expected. (The emulator was already clock-cycle perfect by then.) Here’s the assembly code:

+
INX         ; $E8           ; 232
+STX $D021   ; $8E $21 $D0   ; 142 33 208   ; $D021 = 53281
+JMP $C000   ; $4C $00 $C0   ; 76 0 192     ; $C000 = 49152
+

Here’s the BASIC program to store that program in memory and run it:

+
10 FOR N=49152 TO 49152+6: READ Q : POKE N, Q : NEXT
+20 DATA 232, 142, 33, 208, 76, 0, 192
+30 SYS 49152
+RUN
+

The moral of the story is that you can optimize even a 10-byte program, 15 years after the last time it was used. So don’t tell me that your program can’t be improved, no matter how small it is.

+

PS. I rewrote the code above while writing this article in 2015, about 15 years after the last time I rewrote it. And I again downloaded Vice to test it, this time on Mac OS X.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=282.html b/public/index.html?p=282.html new file mode 100644 index 0000000..af7c0d6 --- /dev/null +++ b/public/index.html?p=282.html @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + +Encouragement – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Encouragement

+ + + +
+

I’ve been on vacation the past week, in Cozumel, Mexico. One day, we went on an excursion called Xenotes. A cenote (say-NO-tay) is a sinkhole filled with fresh water. (The “X” is to give it a more Mayan-sounding trademarkable name.) We had a lot of fun swimming, kayaking, and zip-lining. A bilingual tour guide led our group, which consisted of people from across the US and South America, of various ages and physical abilities.

+

Our tour guide was a lot of fun. He made jokes, told us about the cenotes, and led us in the activities. But he also encouraged us. When someone was scared to do something, he was supportive. He told us that it was okay, and we could do it. But we also felt like it was OK to fail, if we really couldn’t. It really felt like his encouragement was literally creating courage.

+

What was really neat was that despite the language barrier, everyone else was also supportive and encouraging. Everyone cheered with encouragement before someone would attempt something difficult. And we’d cheer especially loud once someone accomplished something that was difficult for their abilities.

+

It was an awesome feeling to feel so supported. It made me feel like I was in a safe place, where I could try new things that were a little past my comfort zone. I was able to do the zip-line upside-down. I jumped off a 15-foot cliff into the water. I even jumped off the cliff a second time, even though I was a little scared.

+

Back at the resort, we played some volleyball in the pool. It was a similar situation, with players of varying ages and abilities. Again, we tried to help the weaker players feel comfortable so they could improve without feeling judged or self-conscious. It helped everyone have a good time. It made everything more fun to be in such a supportive environment, whether I was in a position as one of the more skilled (volleyball), or one of the less skilled (jumping or zip-lining into the water). We were all able to accomplish more, with less effort.

+

These experiences provide a good lesson that can be applied in a lot of places. Such a supportive environment would help any relationship, and any team. I’ve been on a couple really good software development teams, but I don’t think any of them have been as supportive as these two groups of strangers.

+

I’ve decided that this should be one of my goals as a team leader. I want to create an encouraging environment for the whole team. I want to make sure that everyone is comfortable enough that they feel like they can try things that are difficult, even if they might fail (as long as nobody gets hurt).

+

If a group of strangers can do this, so can your team. So can you and your significant others. We need to work every day to make sure that we’re supporting each other. It’s the best way to get everyone to achieve more.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=285.html b/public/index.html?p=285.html new file mode 100644 index 0000000..f6d1def --- /dev/null +++ b/public/index.html?p=285.html @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + +Face Your Fears – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Face Your Fears

+ + + +
+

I’ve always been someone who faces my fears.

+

I have a moderate case of arachnophobia. I don’t run away when I see a spider, but it creeps my out when one is crawling on me. When I was in college, I decided to buy a tarantula to attempt to get over my irrational fear of spiders. I thought I’d be able to get more comfortable with the tarantula over time, eventually to the point of letting it crawl on my arm. It didn’t work. Although I did find that my fear of tarantulas is rational — I got a terrible case of hives just from touching the urticating hairs that fell off into its water sponge.

+

Last week, I was on vacation in Mexico. One of the excursions we took involved jumping in the water a lot. I’m not a strong swimmer — mostly because I have a hard time closing my nose; I hold my nose when I jump in. We zip-lined into the water a lot. At one point, there was a cliff to dive into the water from. It was about 15 feet above the water. It didn’t look so far down before jumping. But it felt like a really long way down the first time I jumped from it. It was pretty scary for me. So I did it a second time. There wasn’t any peer pressure to jump a second time. I literally jumped a second time specifically because I was scared.

+

Fear is a weird thing. Fear is there to protect us. But it’s there to protect us from the dangers of the African savannah. Most of the things our fears protect us from don’t exist in our everyday modern lives. So maybe we should work to gain a better understanding of how our fears work, to figure out when to pay attention to them and when to ignore them.

+

Fortunately, our brain has a good mechanism to help us do this. Our brains basically have 2 main processing systems. The first one is for quick reactions. This one involves things that are nearly reflexes. Fear is in this system. The second system is our analytical reasoning system. This system takes longer to process, but is able to take on more information.

+

Whenever the situation allows us time for both systems to work, we need to listen to them both. We need to listen to our fears, because they’re there for a reason. But that reason might not pertain to our situation. So we need to realize that, and let the slow analytical system determine if we should ignore our fears.

+

If we don’t allow both systems to work, we’re not taking full advantage of our brains; we’re not taking full advantage of the situations that life is presenting to us.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=287.html b/public/index.html?p=287.html new file mode 100644 index 0000000..d720646 --- /dev/null +++ b/public/index.html?p=287.html @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + +2015 Year in Review – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

2015 Year in Review

+ + + +
+

It’s that time of year again — time for a retrospective on how I did on my goals for the year. I had 5 main goals for 2015:

+
    +
  • Job Hunting
  • +
  • Conferences
  • +
  • Blogging
  • +
  • Programming Language Design
  • +
  • Writing an Agile Book
  • +
+

Job Hunting

+

I got pretty lucky on this one. My main contract with Mercy got extended several times. Amos and I must have been doing a good job of keeping the customer happy. We even made it through a couple rounds of layoffs. I’m wrapping up the gig at Mercy now. I’m working one day a week there, as the project winds down.

+

I also started a new gig this month at CenturyLink. I’m working on a cloud development team. Our current project involves selling WordPress as a service. The manager had been courting me for most of the year. I’m excited about my new role; I’ll be writing about it in a blog post soon.

+

Conferences

+

I set a goal in 2014 to give my first conference talk. I accomplished that, giving an ambitious talk at RubyConf. I enjoyed having done that, and vowed to do more conference speaking.

+

I gave 3 conference talks in 2015. I gave a workshop on HTTP at RailsConf. I talked about immutable infrastructure at Madison+ Ruby. At RubyConf, I gave a talk on a micro-ORM I wrote. I also gave a lightning talk about Agile estimation (#noestimates).

+

I was an alternate speaker at Windy City Rails, but did not give my talk on Alternatives to ActiveRecord. I also went to Strange Loop, mainly to see several friends and acquaintances speak.

+

Blogging

+

I wrote 24 blog articles this year. That’s about one every other week. What really kept me going was participating in a writing pact. When the pact was going, I had a 75% blogging rate. That’s pretty good.

+

I’m not so sure about the quality of my blog writing though. I know that practicing writing is supposed to make you better. I know I wrote some really good articles over the past year, but I think I also wrote some articles that weren’t very good. I think sometimes the deadline has caused more harm than good. I’m not really sure what to do about that; perhaps just pushing on is the right answer.

+

Programming Language Design

+

I’ve taken a lot of notes on the design of my programming language. Any time I learn something interesting about another language, or come up with another idea, I write it down.

+

But I haven’t worked on the implementation. (I last worked on the implementation in 2014.) I should be experimenting with some ideas, implementing them to see how they work out. I’ve even kicked around the idea of starting with a Forth variant, just to get something working quickly.

+

I haven’t written any articles on my ideas this year either. My notes are pretty extensive, and it would be good to write some articles to help get my thoughts straight.

+

Writing an Agile Book

+

I’ve got some things to say about Agile, and want to write a book to express those ideas. I’ve made a start — I’ve got the chapters outlines, and have started on a few chapters. But I haven’t made as much progress as I’d like to. I shared what I’ve got with Amos, and he showed some interest in pairing with me on the writing. Hopefully we’ll work on it together in 2016 and publish it.

+

Other

+

There were a few other accomplishments that weren’t explicitly on my list, but I’d like to call attention to.

+

I’ve continued participating on the This Agile Life podcast. I was in 12 of the 33 episodes that were recorded in 2015. I hope to participate in more in 2016. We’re considering scheduling a standard recording night each week, which might help us record more regularly.

+

I recently took over as maintainer of Virtus, a library to declare attributes for Ruby model classes. I haven’t done a lot yet, since I’ve been busy with travel, vacation, and holidays. But I hope to catch up with all the pending pull requests and issues in the next month or so.

+

The accomplishment I’m most proud of is mentoring for the Roy Clay Sr. Tech Impact program. This is a program begun as a result of the Ferguson protest movement. We’re helping teach kids (from 14 to 25) web design and development. My personal goal was to give these kids an opportunity that they would not have otherwise had. But it turned out that some of them have actually started a business building web sites for small companies. I’m so proud of the progress they’ve made in such a short time; it’s a challenging program.

+

Conclusion

+

I’m pretty happy with my accomplishments this year. I made at least some progress on each of the goals I set. I’ve been thinking about my goals for next year; I’ll write that as a separate blog article next week.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=290.html b/public/index.html?p=290.html new file mode 100644 index 0000000..a16bc79 --- /dev/null +++ b/public/index.html?p=290.html @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + +Resolutions for 2016 – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Resolutions for 2016

+ + + +
+

I’ve written up a list of career-related resolutions the past few years. I’ve been pretty successful meeting those goals, so I’m going to continue the tradition.

+

Coding

+

I want to increase the Open Source code I write this year. That’s basically everything I write that’s not directly related to work.

+

I took over maintenance of Virtus late last year, but haven’t done a good job at finding the time for that. That’s probably more responding to issues on GitHub and merging pull requests. But eventually, I’ll likely add some features and do some refactoring.

+

My goal is to work on 6 apps and libraries this year. That’s a pretty aggressive goal, but I want to focus on both learning new things and building some simple but useful things. Think MVP — focus on getting a lot of bang for not too much effort.

+

I want to work on a new Rails app, to familiarize myself with Rails 5. I want to get some experience with some more modern gems. I also want to see if I can use some new techniques with a Rails app, especially things that might help us get closer to a hexagonal architecture. I’m also hoping to work with some different web frameworks — maybe Lotus, Rodakase, Trailblazer, or Phoenix. Or maybe one of the Crystal web frameworks.

+

I also want to work on a couple Elm apps. I started the STL Elm group as an excuse to learn the language. I’d like to work on a Twitter client, that might eventually turn into a more general reading app. Beyond Twitter, it would keep track of things I want to read. The other Elm app I’d like to write is a game, based on the old Space Taxi game I loved to play on the Commodore 64.

+

I wrote a micro-ORM last year called Ruby Preserves. I’ve started converting that from working with raw SQL to sitting atop the awesome Sequel gem. The simplicity is still there so far, and basing it on Sequel has so far gone easier than I expected. But I haven’t figured out how to do relationships (JOINs) yet; that will be the real test.

+

Conferences

+

I’m pretty happy with my 2015 conference experience. I want to keep my conference level about the same. I don’t really want to commit to more talks than I gave last year. Conference presentations take a lot of time and energy to write and practice.

+

I’m planning to go to RailsConf, RubyConf, and Strange Loop. I’m hoping to give a talk at each of those, if I can. But I’ll probably attend all of them even if I’m not speaking. I’m also submitting a talk to Agile 2016. I’m unlikely to go to any other conferences, and even more unlikely to give more than 4 conference talks.

+

Writing

+

I really want to finish my Effective Agile book this year. I’m going to try to pull Amos in to pair with me on it occasionally, to move it forward. If I’m able to get it done, I’d also like to maybe write a book on Elm.

+

I actually want to do less blogging this year. Well, sort of. I want to put coding and book writing ahead of blogging. If that means less blogging, I’m okay with that. So my goal for blogging is more like every other week. And when I do write a blog article, I’d rather it be related to some code I’m working on, something that I could use in my book, or some more in-depth thoughts about programming language design.

+

Job Hunting

+

The contract I recently started at CenturyLink Cloud is open-ended, so I’ll likely be there for the entire year. I’m enjoying it so far; it’s a challenge, but I think I can make a significant impact. However, I’m really interested in moving to San Francisco in 2017. So I’m going to work on preparing for that in several ways.

+

I’m going to resurrect my LinkedIn account. I’ve neglected it for a few years now. I’ll make sure everything is up to date, and make some connections I’ve been putting off. I also want to reclaim my Stack Exchange and Hacker News accounts.

+

I’m going to pay a an expert to help me revamp my résumé. I think it’s pretty decent, but it could use some freshening up. I want it to stand out. I want to do a better job of explaining what I really do — which is to join a team to help them improve their processes and their technical skills.

+

I’ll also need to clean up all my web sites, so they look nice when people come to take a look at them. This includes my personal site, consulting business site, wiki, and blogs. I should also upgrade my server to the latest version of Debian, and use Ansible and some other tools to provision it using the principles of Infrastructure as Code.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=294.html b/public/index.html?p=294.html new file mode 100644 index 0000000..d4b3ca1 --- /dev/null +++ b/public/index.html?p=294.html @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + +Team Values – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Team Values

+ + + +
+

I held a retrospective with my new team last week. The team includes 2 senior developers, 2 junior developers, a product owner, and a product analyst. I’ve joined the team as an engineering manager, which I think of more as a team lead with an elevated title. Being new to this group, I wanted a way to understand their values. What motivates them? What common values do we share that we can leverage to move forward in the same direction?

+

I started out with a pretty simple question: “What do you value (in regards to what we’re building); what are you willing to fight for?” I asked them each to write down several values and then put them on the board before looking at everyone else’s answers. I also asked each person to rank their values in order of importance.

+

It turned out that my question was a little vague. Some people thought about the question in terms of the end result, and some in terms of the process of creating the software. In some ways that was a bit of a problem, because the different interpretations led people in different directions. But in other ways, not pushing them in any particular direction got more varied answers, exposing how they think about the project and the product.

+

My list (in order) was Effectiveness (doing the right thing), Quality (doing things right the first time), Happiness, Purpose, and Teamwork. In retrospect, I should have put Happiness first. If I’m not happy at work, I don’t really want to be there, and need to move on. (Sometimes I can trade some happiness at work for more happiness at home, but I’m definitely a person that needs to be happy at work.) The other values serve to improve my happiness, but the happiness is more important.

+

My own issue ranking my values turned out to be a problem with the ranking in general. Should they be ranked in importance of the necessity of the value as an outcome, or in importance of the necessity to focus on the value? I think the former is what I was looking for, but even I wasn’t clear on that when I began the exercise.

+

After everyone put their values up on the board, we read them off. Then I asked the team to choose several values that we share as a team. We pulled them off the individual members’ lists, and put them in the team list. Then I asked each person to come up and rank those values, then explain why they had ordered them that way, especially when they ordered them significantly different than the last person. I was hoping to come to some convergence of the rank over time, so we could document our values in rank order. That didn’t happen. But the discussion was illuminating to me and to everyone on the team.

+

I think the most interesting part about the lack of convergence was the difference between the developers and the product guys. The product guys definitely viewed the values more in terms of outcomes than the process. That makes sense — they’re not as intimately involved in the process of building the product.

+

We were able to converge on the top priority though: Will the user buy the product? This was a combination of a couple different values that we merged together. This included the end user experience as well as making sure the team would continue to have a reason for existing. The rest of the values we left unordered: Teamwork (cohesiveness), Data driven decisions, Team ownership, Simplicity, Effectiveness, Maintainability / Supportability, Quality, Automation, and Performance. I think that’s a pretty decent list.

+

As a couple teammates pointed out, those values are probably in part a reflection of this current point in time. If I asked the same question some other time, under different conditions and team dynamics, the answers would probably change a bit. And we’d probably come up with other answers if asked again, just due to randomness of the way we think about these things.

+

But I don’t think I’d do this activity a second time with the team. It was really about understanding our motivations — both our own, and those of our teammates. I found it effective in that way, and also in helping the team to think about our culture and how we can work to shape it to help us all push in the same direction.

+

There are a few caveats. When I asked for feedback on the exercise, one teammate pointed out that it wouldn’t work if people weren’t honest about their values, and they answered with what they thought management or their teammates wanted to hear. I don’t think that was an issue with this group, but it’s something to keep in mind.

+

The other major thing I’d do is to make it clear up front that I’m not looking for any action items from this activity; it’s more about understanding each other and ourselves. And next time, I’ll work to clarify how to rank the values.

+

I would recommend this activity for a new team, or when the makeup of the team is changing in some significant way. I wish there was a way to help a team converge on the ranking of their values, but I suppose I should be happy that agreeing on the set of important values went pretty quickly. And the diversity of ideas and opinions is probably a blessing that I should be embracing — the more ideas we have, the wider the variety of solutions we can imagine.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=307.html b/public/index.html?p=307.html new file mode 100644 index 0000000..864ebbe --- /dev/null +++ b/public/index.html?p=307.html @@ -0,0 +1,294 @@ + + + + + + + + + + + + + + + + +My First Open Space – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

My First Open Space

+ + + +
+

I recently attended an Open Space hosted at work. I’d never been to an Open Space, and didn’t know what to expect. We’d been told that this was a workshop to help Engineering Managers (my role), Product Owners, and Product Analysts find better ways of working together. But due to the way an Open Space works, it evolved into something completely different — and better.

+

We’d brought in Diana Larsen to facilitate the Open Space. Diana is a stalwart of the Agile community, focusing on how people and teams interact. She literally (co-)wrote the book on Agile retrospectives. Diana was also kind enough to be our guest at an Agile LINC meetup later in the evening.

+

The morning started off with all the participants sitting in chairs arranged in a circle. Diana rang a chime, a nice soothing tone that literally set the tone for the day. (Most people didn’t seem to like it, but I thought it had its purpose.) It also acted as a call to gather in the meeting space. She then walked around the inside of the circle as she explained what was going to happen.

+

The idea behind Open Spaces came with the realization that at a conference, the “hallway track” (ad hoc discussions in the hallways) was often more valuable than the scheduled talks. So they figured out a way to capture that experience. There are only a few rules:

+
    +
  • Whoever shows up is the right group
  • +
  • Whatever happens is the only thing that could have
  • +
  • Be prepared to be surprised
  • +
  • Whenever it starts is the right time
  • +
  • When it’s over, it’s over
  • +
  • The Law of 2 Feet: If you’re not learning or contributing, go somewhere else
  • +
+

Once Diana set the scene and explained the rules, people came up and presented ideas for topics. If you proposed a topic, you chose a time and location on the topic board. At that time, you’d facilitate a discussion on that topic. The format was really conducive to discussions. There was no preparation, so discussions were from the heart — lots of people felt that they could contribute.

+

Being new to the company (only 2 months), I got a lot out of the workshop, beyond the content itself. I got to get to know several more people. I’d even say I made a few friends. Having open discussions, we found that many of the teams were having the same issues. This made it easy for me to talk to other people.

+

All-in-all, I found the Open Space to be extremely conducive to discussions aimed at identifying issues, brainstorming solutions, and planning action items. I felt like we made great strides in addressing some of the biggest problems our organization is currently facing.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=313.html b/public/index.html?p=313.html new file mode 100644 index 0000000..80a5c0e --- /dev/null +++ b/public/index.html?p=313.html @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + +You Don’t Have to Be Right – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

You Don’t Have to Be Right

+ + + +
+

Not too long ago, I was asked during a job interview “how do you convince your teammates that you’re right?” I answered with probably the most surprising answer: “I don’t.”

+

It’s taken me years to realize that being right isn’t terribly important. Especially when there’s more than one right answer — which there usually is. It’s more important to work as a team. It’s more important to be respected and to have respect for others.

+

A few weeks after that interview, I was pair programming with the person who had asked me the question. We’re both senior developers, so we both have a lot of experience and opinions based on that experience. We were writing a shell script, and we both had an idea in mind about how to write the code. I let him proceed with his idea. It was a pretty good idea; probably better than mine. But we paired very effectively. I’d let him finish some code, and I’d find a way to make it better. Then he’d find a way to make my code better. What we ended up with was so much better than my original idea, and his original idea as well. I commented on this, and he agreed. We left that pairing session feeling the high of having accomplished something rare — two very experience programmers coming up with better code than we could have imagined going in.

+

I like to think of writing code as a path towards a destination. The destination is good code that does what it’s supposed to, is readable (intention revealing), is concise, and is well-factored so as to be easy to change in the future. But there are many paths to that destination. Especially when pair programming, the code gets refined as ideas are shared, getting you closer and closer to that destination. Sometimes the different paths end up with the same code; sometimes they end up with different code. But if the code does its job well (according to those criteria), it doesn’t really matter what code you ended up with.

+

Thinking back to the original question, I think the answer is a little more nuanced. (I’m pretty sure I followed up with an explanation, but I don’t recall the details.) It truly doesn’t matter in a lot of cases. If there are different paths that lead to the same destination, and they’re all roughly equal, then my rule works. It can also work even if the first path you try (a “wrong” path) doesn’t work out, as long as it’s easy enough to try a different path. I find that the majority of day-to-day coding fits these conditions.

+

But there are some places where it’s costly to get things wrong the first time. In these cases, it’s worth spending some time thinking about and discussing the alternatives before getting started. Software architecture is the big one that comes to mind. In general, weighing the pros and cons of each option and applying previous experience works best.

+

So next time you think about trying to convince someone you’re right, fight the urge. Instead, let them show you what they’re thinking. They just might surprise you. And more importantly, you might surprise yourself.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html?p=76.html b/public/index.html?p=76.html new file mode 100644 index 0000000..86cab3a --- /dev/null +++ b/public/index.html?p=76.html @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + + +My Thoughts on Python vs. Ruby | BoochTek, LLC + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+
+ + + + + +
+
+

My Thoughts on Python vs. Ruby

+ + +
+ +
+

I’ve been using Python at work for the past few months.  I learned Python back in the early 2000s, but never used it for any large projects.  I learned Ruby in late 2005, and it quickly became my language of choice for most cases.

+

So while I still prefer Ruby, and will likely use Ruby more in the future than Python, I wanted to assess the strengths and weaknesses of Python in relation to Ruby.  Perhaps some of the lessons could be applied when writing Ruby, and it could help to decide when to use each.  Also, I’m interested in programming language design, and wanted to document pros and cons in that light.

+

Where Python Sucks (As Compared to Ruby)

+
    +
  • Have to explicitly include self in EVERY method declaration. +
      +
    • Including @classmethod declarations (although people usually use the name cls instead of self there).
    • +
    • Except @staticmethod declarations.
    • +
    +
  • +
  • Have to use self everywhere to reference an object’s own attributes.
  • +
  • Inconsistency of things like the len() function, when everything else is a method.
  • +
  • Inconsistency of having some built-in classes with lower-case names. +
      +
    • And they’re not whole words, so you have to memorize them: list, dict, str, int.
    • +
    • Even more bizarre is dict vs. OrderedDict.
    • +
    +
  • +
  • I’m not a fan of True and False being uppercase. +
      +
    • I’m a bit less concerned about None, for some reason.
    • +
    • If they’re going to be uppercase, they might as well be all caps, in my opinion.
    • +
    +
  • +
  • Inconsistency of camelCase versus snake_case in some modules.
  • +
  • Claims to prefer explicit over implicit, yet does not use new to create an object. +
      +
    • It is nicely concise, but makes it look too much like a regular function call.
    • +
    +
  • +
  • The implementation of lambdas is too limited. +
      +
    • Makes using map function not very useful.
    • +
    +
  • +
  • Class methods are less than ideal to implement. +
      +
    • Probably better to use functions within a module instead, in many cases.
    • +
    +
  • +
  • Lack of good functional programming tools makes it harder to manipulate lists. +
      +
    • Have to often resort to creating an initial list and adding to it in a for loop.
    • +
    +
  • +
  • I don’t understand why r’regex_string’ doesn’t just create an actual regular expression object.
  • +
  • I miss Ruby’s method-call-or-property-getter syntax. +
      +
    • Nice that I can get it by adding @property to method definitions, but that’s a bit messy.
    • +
    +
  • +
  • I don’t understand why lists don’t have a join() method; it seems backwards to call join on the string used to connect the list elements.
  • +
  • I miss unless; seems like with all the keywords, Python would have added that.
  • +
  • I really miss ||= to memoize. +
      +
    • The distinction between unset variables and variables set to None makes it hard.
    • +
    • I also miss +=.
    • +
    +
  • +
  • I really miss statement modifiers (if or unless at the end of the statement/expression instead of the beginning).
  • +
  • I miss being able to assign to a compound statement (x = if True: 1; else: 2). +
      +
    • While Python does allow this simple case, it does not allow more complex cases.
    • +
    +
  • +
  • The way modules, files, and classes work, I either have a lot of classes in one file, or have to come up with more module names in order to split classes into different files.
  • +
  • The preference for bare functions within modules over class methods often leads to functions outside of classes, when they’d make more sense more closely associated with the class.
  • +
  • I don’t quite understand the necessity for pass. +
      +
    • Seems like allowing 0 lines of indented code would suffice instead.
    • +
    +
  • +
  • I miss implicit return. +
      +
    • Explicit return looks a bit nicer, but is less concise, and I’ve gotten out of the habit.
    • +
    +
  • +
  • Mixins turn out to be more useful than multiple inheritance.
  • +
  • I miss string interpolation.
  • +
  • I really miss Array#first and Array#last.
  • +
  • I really don’t like that 0 is falsey. +
      +
    • I could live with empty lists and dicts being false, but not 0.0 and 0.
    • +
    +
  • +
  • Comparing a string to a regular expression seems harder than it should be.
  • +
  • Converting to a Boolean is not as easy a Ruby’s !! syntax. +
      +
    • OK, bool(expression) isn’t so bad, I guess.
    • +
    +
  • +
+

Where Python Rocks

+
    +
  • Indentation removes the need for end everywhere.
  • +
  • Import statements are nice. +
      +
    • Can import everything, or just a few things.
    • +
    +
  • +
  • Annotations are nice for aspect-oriented modification of methods. +
      +
    • Does it make it hard to debug though, like alias_method_chain in Ruby?
    • +
    • It’s kind of tricky to write them though, due to doubly-nested function definitions.
    • +
    +
  • +
  • List comprehensions are more powerful than map. +
      +
    • But map can be more concise for the most common cases.
    • +
    +
  • +
  • Can add arbitrary attributes to any object, class, or module.
  • +
  • The object creation syntax is nicely concise.
  • +
  • Sometimes the ability to add a bare function within a module is nice.
  • +
  • Keyword arguments are nicely done. +
      +
    • Ruby’s emulation via hashes without braces is OK, but the corner cases are problematic.
    • +
    +
  • +
  • Docstrings as part of the language is nice.
  • +
  • The new with keyword looks like a halfway-decent replacement for blocks. +
      +
    • Seems like a lot more work that using blocks and yield though.
    • +
    +
  • +
  • Python 3 drops support for octal literals starting with a 0. +
      +
    • Still allows it via a 0o prefix.
    • +
    +
  • +
  • No support for all the crazy Perl-inspired globals.
  • +
  • The names list and dictionary are better than array and hash. +
      +
    • The Ruby names are more about the implementation, especially hash.
    • +
    • I’d much prefer the name map over hash, or even dictionary.
    • +
    • The name list is only slightly better than array.
    • +
    +
  • +
  • List and string slicing is quite nice. +
      +
    • I do wish that the syntax was “..” instead of “:” though.
    • +
    • Slice assignment is even cooler.
    • +
    +
  • +
  • I prefer the Python dict syntax over the Ruby hash syntax (“:” vs. “=>”). +
      +
    • The Ruby 1.9 symbol hash syntax is an improvement, but not quite as good.
    • +
    +
  • +
  • Checking for string containment is nice: if substring in string.
  • +
+

Where Ruby Rocks

+
    +
  • Consistency.
  • +
  • Blocks.
  • +
  • Excellent functional tools to deal with Enumerable.
  • +
  • Meta-programming.
  • +
  • Optional parentheses.
  • +
  • Modules and classes are also objects.
  • +
+

 

+
+ + +
+ +
+ + + +
+

Leave a Reply

+
+

Your email address will not be published. Required fields are marked *

+ +

+

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

+ + + +

+

+
+ +
+ + +
+
+ + +
+ + +
+ + + + + \ No newline at end of file diff --git a/public/index.html?p=93.html b/public/index.html?p=93.html new file mode 100644 index 0000000..f62a570 --- /dev/null +++ b/public/index.html?p=93.html @@ -0,0 +1,307 @@ + + + + + + + + + + + + + + + + +Testing Rails Validators – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ +
+
+

Testing Rails Validators

+ + + +
+

It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+
+ + +
+ +
+ + + +
+

Leave a Reply

+

Your email address will not be published. Required fields are marked *

+ +

+

+ +

+
+ +
+ + +
+ + + +
+ + + + +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/public/page/2.html b/public/page/2.html new file mode 100644 index 0000000..ca67fc2 --- /dev/null +++ b/public/page/2.html @@ -0,0 +1,763 @@ + + + + + + + + + + + + + + + +BoochTek, LLC – Page 2 – Web Development, Ruby on Rails, Open Source + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + +
+
+ +

Impromptu Retrospective

+ + + +
+

I’m surprised that I haven’t gotten this story down in print before. It’s something I’ve mentioned many times — including a few times on the podcast. It’s a great story about the power of retrospectives, and it’s a great story about the power of a blameless post-mortem.

+

I don’t recall all the specifics at this point. It was about 5 years ago. I’d just noticed that Arun had made some sort of mistake. That’s fine, people make mistakes. The thing that was different about his mistake was that I had made the same mistake about a week prior. And Amos had made the same mistake about a week before that.

+

Noticing a pattern of mistakes, Amos and I called an impromptu retrospective. We gathered all the developers into a conference room. We explained the problem that we were running into. At first, Arun was defensive. That’s understandable; he thought we were there to come down on him, to lay blame. But we made it clear that we weren’t focusing on him. We admitted that we had also made the same mistake recently. We weren’t there to lay blame; we were there to figure out how our team could stop making the mistake. It took Arun a few minutes to get over the defensiveness.

+

With the defensiveness out of the way, we could focus on the issue at hand. We were able to figure out the root cause of us all making the mistake. (I don’t know if we played the “5 whys” game, but I’m sure we effectively did something similar.) And with that, we were able to change our process, so that nobody else would make the same mistake again.

+

There are 2 important points to this story. First, you don’t have to wait until a scheduled retrospective to hold a retrospective. This one was impromptu, and it’s the best one we ever had. We saw a problem, addressed it, and found a solution in less than an hour. Had we waited until the end of the week, we would have forgotten some of the details, and wouldn’t have been as effective at solving the problem. Second, when addressing problems, take your ego out of the equation. If you’re in a position of authority, take the blame — but never place blame. Focus on what’s important — solving the problem.

+

And don’t forget the Retrospective Prime Directive:

+

Regardless of what we discover, we understand and truly believe that everyone did the best job they could, given what they knew at the time, their skills and abilities, the resources available, and the situation at hand.

+

 

+
+ + +
+ +
+
+ +

The Problem With Estimates

+ + + +
+
+
+

I’m a big proponent of Agile (mostly XP; I’m mostly anti-Scrum) and I’ve contributed some to the #noestimates “movement”.

+

I don’t really mean that nobody should ever estimate anything. I mean that I’ve never seen useful (fine-grained) estimates anywhere. Here are some of the problems with estimates that I’ve seen frequently:

+
    +
  1. We’re not good at estimating how long things will take. We’re usually optimistic about how quickly we can get things done, and almost always miss thinking about things that will take more time. I’ve never seen a case where a project is completed more quickly than estimated. I’ve only rarely seen fine-grained (story-level) tasks completed more quickly than estimated.
  2. +
  3. Management asks for estimates and then treats them as deadlines. The team then learns to inflate their estimates. Then management learns to reduce the estimates they’re given. Given fudge factors in each direction, the estimate no longer has much reliability. Even if you’re using story points, the point inflation/deflation leads to less consistency and therefore reduced reliability.
  4. +
  5. Estimates that are given are negotiated down, or simply reduced. This leads to the question why you’d ask for an estimate and not take the answer provided. If you’re not going to listen to the answer, why are you asking the question? This is probably the craziest one on the list — given my first point, increasing an estimate would make sense. Reducing the estimates is just magical wishful thinking.
  6. +
  7. Plans change and work is added, but the deadline (presumably based on the estimates) is not changed to correspond with the extra work involved. So again, you’re not actually even using the estimates that were given.
  8. +
  9. Management dictates deadlines arbitrarily, without speaking to the people who will be doing the work. Spending time estimating how long each task will take when the deadline is already set is completely pointless.
  10. +
  11. Almost every deadline is complete bullshit, based on nothing. Often the excuse is that marketing needs to know when something will come out, so that they can let people know about it. Why they need to know the exact release date way in advance, I’ve never been able to figure out. Many people intuitively know that the deadlines are bullshit, and will likely be allowed to slip. The only exception to bullshit deadlines I’ve come across are regulatory deadlines. (I know there are a few other exceptions out there.)
  12. +
  13. Estimation at a fine-grained level isn’t necessary. Many Agile teams estimate using story points, and determine a conversion from story points to time based on previous empirical data. This is fine, except that the time spent estimating the story is wasted time — counting the number of stories almost always gives the same predictive power. Teams tend to get better at breaking up stories over time, so that they’re more consistent in size, so this becomes more likely over time.
  14. +
  15. The ultimate purpose of an estimate is to evaluate whether the proposed work will be profitable, and therefore worth doing. Or to compare the ROI (return on investment) between alternative projects. But to know that, you’ll have to know what value that work will provide. I don’t believe I’ve ever seen that done — at least not at a fine-grained level. Usually by the time you’re asked to estimate, the project has already gotten approval to proceed.
  16. +
+

I’ll note that most of these pit management against the team, instead of working together toward a common cause. Most of the practices also lead to seriously demoralizing the team. And most of the time, the estimates aren’t really even taken into account very much.

+

My advice is to first understand the value of a project before you consider estimating the costs. The estimation at this point will be very rough, so make sure that you have a very wide margin between the expected value and the rough estimate of the cost. If you’re pretty certain of the expected value, I’d probably want to make sure I could still be profitable even if it took 3 or 4 times as long to complete as the rough estimate. And if there’s uncertainty in the expected value, much more.

+

Another way to mitigate the risk of throwing money at something that’s not going to have positive ROI is to reduce the feedback loop. Order the work so that the tasks are ranked in order of value to the customer. (Realistically, you’ll have dependencies of tasks to worry about, and should consider effort involved too.) So work on the most valuable feature first — get that out into production as soon as possible. Once that’s done, you can assess if your ROI is positive or not. Keep iterating in this fashion, working on the features that will provide the most value first. Keep assessing your ROI, and stop when the ROI is no longer worth it, compared to other projects the team could be working on.

+

At a fine-grained level, if you’re using story points, I’d ask you to do the math to see if just counting the stories would be as effective at predicting how much will be done over time as using the story points. If so, you can save the time the team spends on estimating stories. I’d still recommend spending time talking about stories so that everyone has a shared understanding of what needs to be done, and to break stories up into a smaller, more manageable size — with one acceptance criteria per story. Also take a look to see if empirical average cycle time (how long it takes a single story to move from start to finish) might provide you the predictive power just as well as estimates. (I.e. is it bandwidth or latency that really provides the predictive power you’re looking for?)

+

And don’t forget Hofstadter’s Law: It always takes longer than you expect, even when you take into account Hofstadter’s Law.

+
+
+
+
+
+
+ + +
+ +
+
+ +

Website Checklist

+ + + +
+

While creating a web page isn’t too difficult, there are a lot of moving parts involved in creating an effective website. We’ve written up this checklist as a guide for anyone who has decided that they need a website.

+

This list is for a basic static website. A web application will require significantly more effort. We’re working on a separate checklist for web apps.

+

Overview

+
    +
  • Determine your goals
  • +
  • Develop a business plan
  • +
  • Register a domain name
  • +
  • Set up DNS hosting
  • +
  • Set up web hosting
  • +
  • Design and develop the site
  • +
  • Deploy and test the site
  • +
  • Market your site
  • +
  • Set up additional services
  • +
  • Maintenance
  • +
+

Goals

+
    +
  • What do you want to achieve from having the site?
  • +
  • What “call to action” do you want visitors to take?
  • +
  • How will you measure success?
  • +
  • What will be the focus of the site? +
      +
    • Info (“brochure”) for an existing business
    • +
    • Blogging
    • +
    • Sales
    • +
    • Community
    • +
    • Web app
    • +
    • Mobile app
    • +
    +
  • +
+

Business Plan

+
    +
  • Who is your target audience?
  • +
  • Marketing +
      +
    • How will you get them to come to your site?
    • +
    • How will you get them to buy something?
    • +
    +
  • +
  • Who is your competition? +
      +
    • How do you compare to them?
    • +
    • How do you differentiate from them?
    • +
    • What is your niche?
    • +
    • What makes your site better than theirs?
    • +
    +
  • +
  • How will you make money? +
      +
    • Bringing people into brick and mortar business
    • +
    • Advertising
    • +
    • Periodic billing/subscriptions
    • +
    • Selling goods/services
    • +
    • Get bought out
    • +
    +
  • +
  • Pricing +
      +
    • Aim high — it’s easier to lower prices than raise them
    • +
    • Tiered pricing often makes sense; most people pick the middle tier
    • +
    +
  • +
  • What will it take to have a positive ROI?
  • +
+

Domain Name

+

You’ll probably want your own domain name.

+
    +
  • Think of a name +
      +
    • Stick with a .com name if possible; don’t use .biz
    • +
    • Some other top-level domains come into fashion on occasion +
        +
      • .io is pretty popular right now
      • +
      +
    • +
    +
  • +
  • Check availability of the name
  • +
  • Keep checking names, until you find a good name that is available
  • +
  • Register the name with a respectable registrar +
      +
    • DNS Registrars run from $10-$35/yr
    • +
    • DO NOT allow your web host provider to own/control your name
    • +
    • You may want to grab the .net, .org, and other versions of the same name
    • +
    • Multiple-year registration is cheaper, but it’s easier to forget how to renew it
    • +
    +
  • +
  • Your registrar will likely point your domain to an “under construction” page initially
  • +
  • DO NOT LOSE YOUR NAME! +
      +
    • Spammers and pornographers will take it over if your registration lapses
    • +
    • Make sure your contact info (especially email address) is up-to-date
    • +
    +
  • +
  • Beware scams (usually by US mail) trying to get you to renew with a different registrar
  • +
  • Note that the email address you register with will get spammed +
      +
    • Some registrars provide some protection for a fee
    • +
    +
  • +
+

DNS Hosting

+

You’ll need to have someone take care of the servers that tell the world what server addresses your domain name corresponds to.

+
    +
  • Find a DNS hosting provider +
      +
    • We like DNSimple; they also do domain registration
    • +
    +
  • +
  • Provide the name servers to your DNS registrar
  • +
+

Web Hosting

+

You’ll need servers to host your web site on. There are a lot of options available, from virtual hosts (a Linux server where you control everything) to application-specific services.

+
    +
  • Talk to your developer or designer first! +
      +
    • Web host can significantly restrain the development environment
    • +
    +
  • +
  • Cost +
      +
    • $10 to $500 / month is typical
    • +
    +
  • +
  • Bandwidth +
      +
    • Number of users × how often they use it × average “page” size
    • +
    • What happens if you go over?
    • +
    +
  • +
  • Up-time +
      +
    • What kind of down-time can the site sustain?
    • +
    • Higher guaranteed uptime costs more
    • +
    • What if the specified uptime is not met?
    • +
    +
  • +
  • Development environment +
      +
    • What programming languages are installed?
    • +
    • What databases are installed?
    • +
    • What libraries are installed?
    • +
    • What if other libraries are required?
    • +
    +
  • +
  • Shared/dedicated/virtual hosting +
      +
    • Shared means others are using the same machine, with security implications
    • +
    • Dedicated is expensive, but you “own” the whole machine
    • +
    • Virtual is somewhere in between +
        +
      • You have a “slice” of a machine dedicated to your site
      • +
      +
    • +
    +
  • +
  • How responsive is the host to problems and requests?
  • +
  • Backups +
      +
    • What do they back up?
    • +
    • How often do they back up?
    • +
    • How can files be restored?
    • +
    +
  • +
+

Design and Development

+

Designing and developing the site can vary from picking an existing template and adding content, to developing a full web application.

+
    +
  • Cost ($30 – $300 / hr)
  • +
  • Project management +
      +
    • Story/task management
    • +
    +
  • +
  • Revision control +
      +
    • Ensures changes can be rolled back quickly
    • +
    +
  • +
  • Functionality +
      +
    • What does the site need to do?
    • +
    +
  • +
  • Usability +
      +
    • How easy is it to use each page?
    • +
    • Is it easy to navigate the site to find what you’re looking for?
    • +
    • Check for broken links
    • +
    • Check for ADA/508 compliance
    • +
    • Spell checking and grammar checking
    • +
    +
  • +
+

Deploying and Testing

+
    +
  • Can updates be deployed quickly? +
      +
    • Deploy early and often, so it’s not such a big deal, and it becomes routine
    • +
    +
  • +
  • Consider a staging and/or beta site +
      +
    • Test everything thoroughly on staging site before deploying to production
    • +
    • Staging site should (mostly) use the same code as production
    • +
    • Staging/test site should not process credit cards, etc.
    • +
    +
  • +
  • Automated testing +
      +
    • Prevents regressions
    • +
    +
  • +
  • Exploratory testing +
      +
    • See how things work
    • +
    • See if you can break things
    • +
    +
  • +
  • Security testing +
      +
    • Penetration testing
    • +
    +
  • +
  • Performance +
      +
    • Load testing
    • +
    +
  • +
  • Beta testers
  • +
+

Marketing

+
    +
  • Search engine “optimization” (SEO) +
      +
    • Good design, good URLs, and well-written HTML should cover most of this
    • +
    • Submit site to search engines
    • +
    • Use robots.txt and site maps
    • +
    +
  • +
  • Directories
  • +
  • PR sites
  • +
  • Targeted groups
  • +
  • DO NOT send out spam
  • +
+

Additional Services

+

What other services do you need, besides just the web site?

+
    +
  • Email
  • +
  • Blog
  • +
  • Wiki
  • +
  • File storage
  • +
  • Forums
  • +
  • Authentication
  • +
  • Customer interaction +
      +
    • CRM
    • +
    • Feedback
    • +
    • Bug tracking
    • +
    • Customer Q&A (StackExchange)
    • +
    • Follow-up emails to customers to offer assistance
    • +
    +
  • +
+

Maintenance

+

Over the lifetime of the site, you’ll likely pay more in maintenance costs than the upfront costs.

+
    +
  • Responding to user emails
  • +
  • Requests for info
  • +
  • Feedback about the site
  • +
  • Password resets?
  • +
  • Tracking bug reports and feature requests
  • +
  • Site improvements
  • +
  • Additional content
  • +
  • Moderation of user content +
      +
    • Spam removal
    • +
    +
  • +
  • Log analysis +
      +
    • Google Analytics
    • +
    +
  • +
  • Assessing advertising effectiveness
  • +
  • Analysis of revenues/profitability
  • +
  • Upgrades +
      +
    • New/improved functionality
    • +
    • Bug fixes
    • +
    • Upgraded infrastructure
    • +
    +
  • +
  • Down-time +
      +
    • Web host
    • +
    • Upgrades
    • +
    • Accidents
    • +
    • Bugs
    • +
    +
  • +
  • Backups +
      +
    • Testing restoring from backups
    • +
    +
  • +
  • Payments for services +
      +
    • Domain name registration – DO NOT LOSE YOUR NAME!
    • +
    • Web hosting
    • +
    • Marketing/advertising
    • +
    +
  • +
+

 

+
+ + +
+ +
+
+ +

Potential F5 Vulnerability

+ + + +
+

It all started with an email about a WebInspect report. It listed a buffer overflow, which we had marked as a false positive. I read the WebInspect report carefully, and found a note at the bottom that said you could test manually to confirm whether it was a false positive or not. Unfortunately, the manual test listed had a few problems. First, it jammed the lines together, without the proper line-breaks. Second, it assumed the site was using HTTP, not HTTPS, so used telnet. Third, it was testing against a page that didn’t exist, giving a 404. Keeping those in mind, I tried the manual test using the openssl s_client command:

+
openssl s_client -quiet -connect mysite.example.com:443
+POST /login HTTP/1.1
+Host: mysite.example.com
+Transfer-Encoding: chunked
+
+c00000000
+

The test terminated immediately. The report said that this meant that the server was vulnerable to a buffer overflow and arbitrary code execution.

+

At first, we thought this was caused by Apache or Tomcat, or possibly the application code. But the reported vulnerability was an Apache CVE from 2002 (CVE-2002-0392), fixed by vendors long ago. After a while, we realized that if we hit the servers directly, we did not get the indication of a vulnerability. If we hit the site through the F5 VIP, we saw the immediate termination of the connection. The issue is with handling of HTTP chunked encoding. Nginx had a similar issue in 2013 (CVE-2013-2028).

+

So we turned our attention to the F5 load balancers. We were able to confirm that other sites using F5 load balancers were exhibiting the same behavior. We also confirmed that WebInspect run against the servers directly did not show the issue (even as a false positive). We reported the issue to F5, and they are looking into it.

+

I’m disclosing this publicly now for a few reasons. First, I’m not a security researcher, and almost nobody follows my blog — especially people looking for security issues that could be exploited. Second, I’ve not developed a payload, so I have no idea whether this is exploitable. But at this point, it’s merely a potential vulnerability. I’m not sure I’ll even spend the time to research and create a payload to prove the vulnerability. If I do, I’ll be more careful with the disclosure.

+

 

+
+ + +
+ +
+
+ +

Not Quite Callbacks

+ + + +
+

I’ve been working on application architectures based on Uncle Bob’s Ruby Midwest talk, following the hexagonal architectural pattern. I posted an article a couple months ago showing a way that works fairly well in Rails, and some accompanying Rails example code. But there was one thing I wasn’t quite happy with.

+

The problem is that we used callbacks (actually, a publish/subscribe mechanism) in a situation where they don’t seem to quite fit:

+
  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+

What we really want is to respond in different ways, depending on the result of the call to interactor.get(). There’s no good reason to define the responses before the call. It makes a lot more sense to define the responses after the call, because they’ll happen after the call. I’d much prefer that the code be written in the order that it will be run.

+

I discussed this problem with my friend and colleague, Amos King. We came up with a better solution, which puts things back in the right order:

+
  def show
+    interactor.get(params[:id]) do |on|
+      on.display { |order| render order }
+      on.not_found { |order_id| render status: 404 }
+    end
+  end
+

He even wrote a small library to do this, which he called Riposte. I’m not sure what to call this pattern, but it seems to work pretty well in this situation. I suppose that they’re still technically callbacks, because they’re passed in in the block that’s passed in to the call to interactor.get(). But due to the magic of Ruby blocks, we get to put them in the order they should be.

+

Riposte also gives you the option of using the response object directly, instead of passing a block:

+
  def show
+    on = interactor.get(params[:id])
+    on.display { |order| render order }
+    on.not_found { |order_id| render status: 404 }
+  end
+

This shows that it’s just returning an object, with the twist that the response object has methods that take blocks. The nested blocks variant is really the same thing, except that it’s yielding to the response object instead of returning it.

+

I’ve decide that is the pattern I’d like to use for interactions and their callers within Ruby hexagonal architecture.

+
+ + +
+ +
+
+ +

Architectural Thoughts

+ + + +
+

I’ve started working on my own framework in Ruby in the past couple days. It’s built upon my recent work at understanding Uncle Bob’s Ruby Midwest 2011 talk, and his article on Clean Architecture, and the resulting hexagonal architecture (AKA ports and adapters).

+

Somehow my research in that vein led me to Gary Bernhardt’s Boundaries talk. I’ve read a lot about the talk, and knew about the idea of “functional core / imperative shell”. And I’ve worked with a lot of similar ideas lately. But I believe this is the first time that I actually watched the whole video.

+

Even after having read a lot about similar ideas, it was pretty mind-expanding. Gary’s really good at presenting these kinds of ideas in a simple way.

+

OOP as usually taught includes encapsulation of data together with behavior, with mutable objects. Functional programming separates data and behavior, with mostly immutable data. From experience, encapsulating data and behavior together seems helpful. But experience also shows that immutability is useful. So it would be good to have both of those together. This is something I’ve been thinking for a few years — how best do we get both?

+

Gary calls the combination “FauxO”. Logic and data are still combined, but there’s no mutation. Anywhere OOP would normally have mutation would just generate a new object. There’s no language restriction involved in enforcing immutability — just discipline.

+

But without mutability, it’s hard to do IO and maintain state. So Gary’s solution is to encapsulate as much as possible into an immutable (functional or FauxO) core, and around that, use an imperative (traditional OOP) shell. The functional core contains the bulk of the logic, and the imperative shell is a glue layer that handles the real world, including disk, network, and other I/O.

+

The result of this is that the shell has fewer paths, but more dependencies. The core contains no dependencies, but encapsulates the different logic paths. So we’re encapsulating dependencies on one side, and business logic on the other side. Or put another way, the way to figure out the separation is by doing as much as you can without mutation, and then encapsulating the mutation separately.

+

I love how this naturally breaks things up, so that the core is all testable with unit tests, and the imperative shell is tested with integration tests. And since the shell has few or no logic paths, you get the testing pyramid, with more unit tests and fewer integration tests. The whole thing ends up being quite beautiful. Tests end up being very fast without any extra effort — not even stubbing or mocking. This tells us that things have been decomposed very well — an elegant design.

+

Gary makes the case that immutable objects can be treated as values, and passed across boundaries. Even process boundaries. This is something I’ve noticed as I’ve been working on my own Uncle Bob style hexagonal framework, but nobody in that camp ever mentioned that — they prefer DTOs or something more like hashes. I’m completely against hashes, because of the “stringly-typed” problem. And I don’t see much advantage in a DTO if I’ve got an immutable object; I’d be basically copying the object to an almost identical object. And I’d be losing any laziness possible for derived values within the original immutable object.

+

It’s striking to me how Gary’s image of an imperative shell around a functional core, plus Net, Disk, and State outside of the shell mirror’s Uncle Bob’s concentric circles. Uncle Bob has entities in the middle, surrounded by use cases, surrounded by Web, DB, and UI.

+

Another advantage that Gary shows is that breaking things up this way allows easy concurrency. In his example, he shows using the actor model — either just using threads and queues, or an actor library (or language feature).

+

After several years of thinking about the architectural issues seen in most large Rails apps, I’m starting to come to an understanding of how to combine all these ideas and come up with an architecture that will work better.

+

 

+
+ + +
+ +
+
+ +

From Agile To Happiness

+ + + +
+

The Agile Manifesto was written in 2001, as a way to explain the common values amongst several “light-weight” software development methodologies that had come about. The term “Agile” was chosen as a shorthand for those commonalities.

+

Once “Agile” started to show success, we started to see many people use the term to market their products and services, whether or not they really believed in the values and principles of the Agile Manifesto. It’s gotten to the point where some of us don’t see much value in using the term “Agile” any more. Even some of those involved in creating the manifesto have suggested new terms. Dave Thomas suggests “Agility” and Andy Hunt has started working on something called GROWS. Personally, I’m considering going back to the term “Extreme Programming”, even though I’ve incorporated ideas from other Agile methodologies.

+

It recently occurred to me that Agile, when done “right”, is closely aligned with the happiness of the team members. This is really interesting, because it aligns the interests of the employees and the business — a win-win situation. My next thought was that maybe the next step after “Agile” will be a focus on happiness and motivation.

+

I’ve recently been thinking about personal motivation lately, in the context of team practices. According to Daniel Pink’s book Drive, people are motivated by autonomy, mastery, and purpose. I personally add a fourth that can sometimes trump the other three: identity. And of course, happiness can also be motivating — both in the attempt to achieve happiness and in just being happy. (I suspect that happiness is more of a parallel to motivation than a cause though.)

+

There are a couple different ways that happiness can be achieved at work. The traditional way is for work to be a means to an end. In this view, the purpose of your job is to provide the money to live your life (outside of work) the way that you want to live it. There’s nothing wrong with this way of thinking. But for the lucky few, we can work on something that makes us happy in and of itself. That’s generally done by finding a job doing something that we enjoy.

+

But perhaps that’s thinking about things the wrong way. For one, plenty of people who have gone that route are still unhappy at work. I think a lot of that has to do more with the context surrounding the work than the work itself. Maybe you’ve got a lousy manager. Maybe you don’t like the people you work with. Maybe the tools you have to work with are bad. Maybe the processes add a lot of unnecessary tedium.

+

So maybe we need to find ways to be happier at work. Most of the Agile practices seem to make team members happy. For example, replacing a light-weight process for a heavier process always makes me happy. And I typically leave retrospectives in a good mood. So that’s a good starting point. But we should see if we can take the idea further. If we take employee happiness as a core value, where can we go? What kind of practices would we want to add? Please share any ideas in the comments below.

+
+ + +
+ +
+
+ +

When Should We Do TDD?

+ + + +
+

On a recent episode (78) of This Agile Life, my fellow hosts talked about when to do Test-Driven Development (TDD). They all said that you should always do TDD — at least for anything that will go into production; there’s an exception for experimenting or “spiking”.

+

I wasn’t on that episode, but later commented on the topic. (Episode 83 — which wasn’t really all that great.) My take was slightly different. I said that you should do TDD only when the benefits outweigh the costs. Unfortunately, we usually greatly underestimate the benefits. And the costs often seem high at first, because it takes some time to get good at writing automated tests. Not to mention that both the costs and the benefits are usually hard to measure.

+

What is the cost of writing automated tests? I’ve asked this question before and recorded the answers (inasmuch as we have them) in a previous blog entry. TDD costs us about 10-30% in short-term productivity. The benefit that they found was a reduction in bugs by 30-90%, and a decrease in code complexity by about 30%.

+

But what about when the costs are higher than the normal 10 to 30 percent? One good example of this is when there’s no test framework for the situation you’re testing. This might be a new language or framework that you’re working with. More likely it’s a complex API that you have to mock out. So that could increase the cost of automated testing so as to outweigh the benefits — especially on a short project. I could imagine situations where the cost of writing the mocks could eat up more than the project itself.

+

Another case where we might consider skipping testing is when we’re more concerned about time to market than quality. This is almost always a mistake. Your code will almost always last longer than you expect. (Remember Y2K?) And if the code lasts longer than you expect, that means you’ll have to deal with bugs that whole time. But we have to work with the information we have at the time we make our decisions. And sometimes that might tell us that time to market is more important than anything.

+

One final case I can imagine is when a true expert is coding something that they’re very familiar with. I could picture someone like Uncle Bob writing code (in a language that he’s familiar with) without tests just as effectively as I could write code with tests.

+

But these situations should not be common; they’re edge cases. In almost all real-world cases, TDD is the right thing to do. Don’t forget, TDD is also a design discipline — it helps you design a better API. So keep doing TDD. But as with any practice, don’t do it blindly without considering why we’re doing it. Make sure you understand the costs, and whether the benefits outweigh the costs.

+
+ + +
+ +
+
+ +

Good Enough

+ + + +
+

I ran into some former colleagues recently, from a company where I had worked to help transform the team to be more Agile. They’ve gone through some reorganization and management changes recently. One of the guys said that their team culture has helped them maintain quality in the face of those changes. This struck me as odd, since I had considered the work I had done there as somewhat of a disappointment. While I felt I had made a small dent, I didn’t feel like I’d made a true Agile transformation. Much of what I had taught didn’t seem to “stick”.

+

Later that night I thought about why my opinion of the team and his opinion were so different. There are a lot of reasons why an Agile “transformation” could fail. It could be due to lack of support from management. Or even worse, not getting the team members to buy into the ideas — usually due to fear of change. But while those had some effect in this case, they weren’t really the main issues. Now that I’ve had some time and some distance from that team, I’ve gained some clarity on what the real issues were.

+

I think the reason that I think this transformation was a failure was due to the continued pace of change that true Agile requires. Basically the team experienced “change fatigue”, where everything keeps changing and you feel like you can never catch your breath. But the more interesting thing is how our perceptions differed. He seemed to think that the transformation was a success — they’re doing things better than they used to. I view it as more of a failure, because they basically stopped improving — at least at the pace that I was hoping for.

+

I think this difference in mindset is pretty fundamental. My view of Agile involves continuous improvement — always inspecting and adapting our processes. I suppose that means that my preferred flavor of Agile is “Kaizen“. But other people don’t see improvement and change as a constant — they see each change as a means to an end. I’m starting to realize that neither viewpoint is objectively correct. Maybe they’re both fine viewpoints to have. Maybe I shouldn’t be so hard on myself and view that engagement as a failure, especially if my teammates view it as a success. Maybe perfection is the enemy of the good. And maybe I need to learn to be happy with “good enough”.

+

 

+
+ + +
+ +
+
+ +

The Power of 1%

+ + + +
+

I frequently participate in a podcast called This Agile Life. Recently, a listener asked how much time Agile teams should spend on self improvement. I said 10% to 25%, leaning towards 15% to 20% for most teams. That comes to at least one hour per day, and maybe even more than one day per week.

+

I’m including personal self improvement and team retrospectives in this self-improvement time. This can be as simple as configuring your IDE to make you more efficient, learning to use a tool better, or following up on action items from a retro.

+

That may seem like a lot of time “wasted”. But I think I can justify the cost of all that time.

+

The purpose of spending time on team or self-improvement — the whole point — is to increase our performance and our efficiency. How much improvement can we expect? Can we improve by 1% each week? That doesn’t sound too unreasonable. I think that’s an achievable goal for almost any team, at least on average.

+

Spending 20% of your time to gain 1% doesn’t seem like it’s worth it — until you consider the long term. With compound interest, you’ll be 67% more efficient by the end of a year.1 At that point, you’ll be able to get things done in 59% of the time — saving 41% of the time required at the beginning of the year.2 The following years will show even more progress, as compared to when you started. If 10x programmers exist, continuous improvement is apparently the way to get there.

+

So there’s a pretty good return on investment, even with a small amount of improvement each week. You’ll be significantly more efficient.

+

But efficiency isn’t really what you should aim for. You should aim for effectiveness. You can be efficient in creating the wrong thing. Part of improving should be ensuring that you’re not just building things right, but that you’re building the right things. Build what the customer really needs. Find ways to ask the right questions.

+

Most importantly, find ways to keep improving. It would be a waste of time not to.

+

1: (1.01 ^ 52) – 1
+2: (0.99 ^ 52)

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/page/3.html b/public/page/3.html new file mode 100644 index 0000000..32800e2 --- /dev/null +++ b/public/page/3.html @@ -0,0 +1,587 @@ + + + + + + + + + + + + + + + +BoochTek, LLC – Page 3 – Web Development, Ruby on Rails, Open Source + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + +
+
+ +

Hexagonal Rails Controllers

+ + + +
+

I’ve had a long love-hate relationship with Rails. I love the MVC framework and how it’s improved our speed of writing web apps. But I’ve never really been completely happy with it. I don’t generally agree with most of its opinions. I prefer models that follow the Data Mapper pattern, not the Active Record pattern. This includes separating the persistence layer from the models’ business logic. I prefer Slim or HAML to ERB. I prefer RSpec to Test::Unit or MiniTest. When Merb hit the scene, I was ready to make the jump, until Merb merged with Rails.

+

So inspired by PJ Hagerty’s recent article on alternative Ruby web frameworks, I started thinking about how I’d write a replacement for Rails. I’d definitely keep the basic MVC framework. But I’d also want to implement a more hexagonal architecture.

+

I started sketching out what this would look like, but I ended up starting with a Rails controller and finding the simplest way to make it hexagonal. I really don’t like callbacks, because they make tracing program execution difficult. But I didn’t see any other alternative. I found a simple pub/sub Ruby library called Wisper. It literally has only publish, subscribe, and on methods. (You use on to register single callbacks via blocks, and subscribe to register an object with method names corresponding to the callback names.)

+

The trick was figuring out how to break the controller into 2 pieces. What finally helped me was to find the single responsibilities of the 2 pieces. The Rails controller would remain in charge of managing the web interface, but would delegate to the other piece to handle any application-specific business logic. I decided to re-watch Uncle Bob Martin’s “Architecture The Lost Years” talk, which was the first time I was introduced to the ideas of Hexagonal Architecture. (He doesn’t name the architecture in the talk, but later calls it Clean Architecture.) He does a decent job of explaining how to break these 2 pieces apart. He used the term “interactor” in that talk, so I decided to go with that. He said that Jacobsen calls it a Control Object in Object Oriented Software Engineering, but that’s too close to Rails’s “controller”.

+

So here’s an example of what I ended up with:

+
class OrderController < ApplicationController
+  def index
+    interactor.on(:display) { |orders| render orders }
+    interactor.list
+  end
+
+  def show
+    interactor.on(:display) { |order| render order }
+    interactor.on(:not_found) { |order_id| render status: 404 }
+    interactor.get(params[:id])
+  end
+
+private
+
+  def interactor
+    @interactor ||= OrderInteractor.new
+  end
+end
+
require "wisper"
+require "order"
+
+class OrderInteractor
+  include Wisper.publisher
+
+  def list
+    orders = Order.all
+    publish(:display, orders)
+  end
+
+  def get(id)
+    order = Order.find(id)
+    publish(:display, order)
+  rescue ActiveRecord::RecordNotFound
+    publish(:not_found, id)
+  end
+end
+

I do have a few problems with this solution though. I’m not a fan of the name “interactor” for the business logic. I thought about calling it OrderOperator, or maybe OrderOperations, because it’s really a collection of operations. Perhaps it would be better to separate each operation into a separate class. Trailblazer does it that way. And for more complicated business logic, I would do that too, using the Method Object pattern. But like a Rails controller, there’s a lot in common among all the operations. I feel like a separate class for each operation for each would create too many coupled classes.

+

I’m also uncomfortable with the fact that the controller is delegating almost everything to the interactor. I guess this is OK, but it feels like there’s too little left when every line starts with interactor. I suppose extracting things some more would help mitigate this concern I’ll likely write a small gem to perform that extraction. I expect that that will allow a typical controller to be written in only a few lines. And maybe the same for the interactor side.

+

With the business logic extracted out of the controller, it was really easy for me to write a command-line version of the app. As Uncle Bob says, “the web is not particularly important to your application.”

+

I’ve put the code for this example on GitHub: https://github.com/boochtek/hexagonal-rails. I’ll likely experiment with it some more over the next few weeks and months.

+
+ + +
+ +
+
+ +

Resolutions

+ + + +
+

January kept me pretty busy, so I’m a little late to this. But better late than never. And as an Agile practitioner, I don’t think personal retrospectives should be limited to one time of year.

+

Review of 2014

+

Last year I wrote a blog entry listing my goals for 2014. As far as New Year’s resolutions go, I was relatively successful — about 50% of my goals accomplished. Unfortunately, my Open Source contributions weren’t as strong as I had hoped; while I released some of my own work, I didn’t do much else. I did increase my blogging; getting in on a weekly blogging pact helped immensely. I also increased my participation on the This Agile Life podcast to a level that I’m happy with. But the accomplishment I’m most proud of was giving a presentation at RubyConf.

+

Plans for 2015

+

I’d like to keep things rolling from last year, but crank up a few things. My plans are quite ambitious, so I don’t expect to get everything done by any means. But I think by setting the bar high, I’ll end up with a lot I can be proud of.

+

Job Hunting

+

Late last year, I took the jump into independent consulting. So far, I’ve really enjoyed it, and I’m booked up through April. My wife graduates in May, so we’ve got the possibility of moving if that makes sense. So I’ll be looking for consulting projects in town, but I’ll also be looking at jobs in San Francisco and Chicago. The possibilities are exciting, and I’ll be taking my time to find something just right.

+

Conferences

+

I was incredibly nervous leading up to my RubyConf presentation. Part of that was just the common fear of public speaking. For me, that only kicks in at around 100 people, and this audience was around 250. I think another reason was that I chose a really ambitious topic, and I kept finding more that I wanted to talk about, but wasn’t prepared for. But I think I did a pretty good job presenting an advanced topic. And I was so pumped by the sense of accomplishment as soon as I finished. So I’m hoping to do it more. I’ve already submitted a couple proposals, and plan to submit several more.

+

Blogging

+

I believe that blogging is important for me to get my thoughts down — for myself and to share with others. I was really successful last year when I had a partner to keep me honest, via a pact. So I’ve started up another pact this year, which will hopefully ensure I’ll keep things going. I’ve got a really long backlog of topics, so as long as I keep at it, I’ll have plenty to write about.

+

I also want to move away from WordPress to a static system — probably Middleman. I’ve got 2 major problems with WordPress. First, I no longer trust its security, nor the security of any application written in PHP. Second, it generates HTML every time someone requests a page, instead of when the content is updated. I find that to be a waste of resources, and problematic from a security standpoint. The main problem with moving to a static blogging system is that I really want to allow comments, pingbacks, and tweetbacks. So I’ll have to find a way to integrate those.

+

Programming Language Design

+

Last year I started thinking about programming language design, and started implementing a language tentatively called Brilliant. I’ve done a lot more thinking on the topic, and have a lot of notes. But I haven’t implemented much more yet. This year, I’d like to get my thoughts more organized, and write a series of blog posts on various aspects of language design. The most interesting part seems to be the trade-offs involved in the ways that various language features interact. So I’d like to make some progress on the language implementation, but more importantly, I’d like to get a lot of my design ideas written down.

+

I’m also going to spend a lot of time learning a bunch more programming languages, so I have a better understanding of possible features, combinations of features, and their interactions. I’ve already start with Elixir, Clojure, and Racket. I’m hoping to also look at OCaml, Factor, and Haskell. I’ll probably also take a look at the 2 “Seven Languages in Seven Weeks” books.

+

Agile Book

+

I think people often have trouble getting started with Agile. I started on a book last year, and got down quite a lot of good ideas. But I realized that I’m going to have a hard time organizing all those ideas into something coherent. Still, I’d like to try to get something out there that lets people get started with Agile. My idea is to present a toolbox of practices to get started with and build on that foundation over time with additional practices. Sort of a playbook on how to get started over the first 6 to 12 months and be successful. I want to make some progress on the book, at least enough to decide whether it’s worth the effort to finish it and self-publish it.

+

 

+
+ + +
+ +
+
+ +

TDD Is Alive And Well

+ + + +
+

I went to RailsConf this year, and the very first talk was a keynote by David Heinemeier Hansson (DHH), the creator of Ruby on Rails. The TL;DR of his talk was “TDD rarely has value”. He followed up with a blog post the next day, titled “TDD is dead. Long live testing.“, and 2 more posts. I think this line of thought is terribly misguided, and causing more harm than good. This article is my response.

+

First, I would like to address the good points of the talk. He said that programming is pseudoscience, and that people want to tell us that there’s a secret to being a better programmer. But what it really takes is working hard — reading a lot of code, writing a lot of code, and rewriting a lot of code. He’s right. And I also agree with him that you should forget about patterns for a while when learning to code. Beginners try to throw patterns at a problem instead of letting the patterns emerge where they’re supposed to.

+

I don’t completely agree that programming is a pseudoscience. In some ways it is, but I think it’s more of a craft. It’s a craft, because there’s a lot of science involved, but there’s also an art to doing it well. And like any craft, you’re always working to get better. So to respond to DHH’s stance that “software is more like poetry than physics”, I think it falls
+somewhere in between.

+

With regard to the software engineering practices we use, there really isn’t much science available, mostly because it’s a soft science. That is, it’s really hard to isolate a single variable when comparing code between projects. And nobody has the time or money to write the same code so many times that the differences would be statistically significant.

+

So we don’t have much science on TDD. But we do have some. Here’s a collection of several: StudiesOfTestDrivenDevelopment. And here’s one that explicitly looks are the difference between test-first and test-last: Does Test-Driven Development Really Improve Software Design Quality? What do these tell us? They tell us that TDD costs us about 10-30% in short-term productivity; reduces bugs by 30-90%, and decreases code complexity by about 30%. As Code Complete tells us (in section 20.5, with studies to back it up), improving quality reduces development costs. So, like most Agile practices, this is a case where spending a bit more time in the short term leads to time savings in the long term.

+

The more important lesson in the talk was that you have to do what works best for you and your situation. If TDD doesn’t give better results, then either find out how to make it give better results, or stop using it. As we often say in the Agile world, Agile doesn’t mean that you can stop using your brain. While I think TDD is appropriate in most situations, there are cases where it’s not worth the additional up-front cost. If the most important thing for your project is time-to-market, then not testing might be the right decision for you.

+

To me, TDD provides a bunch of benefits. First and foremost, TDD is a design discipline. It ensures that I think about how my code will be used before I think about how to implement it. This is very powerful in ensuring that the code is well-written from the perspective of other code using it.

+

Tested code provides confidence to be able to make changes without breaking things. If we write tests after the code, we’re less likely to write them. Tests written after the code also tend to test the implementation instead of the desired functionality. What we really want is tests written as a specification. With tests as a specification, we can come back later and understand why code was written. Without tests, or with poor tests, we can’t understand why the code is there; if we want to rewrite it, we don’t have the confidence that we’re not missing something. Writing tests first also ensures that we only write the code that is needed to implement the required functionality.

+

I’m not sure why DHH hasn’t “gotten” TDD. I’m not sure if it’s because he’s  a better coder than average, or if he just thinks in a different way than most of us. I think it’s partly because he doesn’t understand TDD, which he admitted might be the case. And I think he’s conflating TDD and unit testing.

+

DHH is influential in the developer community, especially those newer to Ruby and Rails. People listen to what he has to say. I was happy to see almost every other speaker made fun of DHH’s ideas, and most of the crowd knew better. But there will be a lot of others who will hear DHH, respect his opinions, and not give TDD the try that it deserves. And that’s sad, because it will lead to an overall reduction in code quality in the world.

+

Here are some other people’s thoughts on the matter:

+ +

 

+
+ + +
+ +
+
+ +

Ruby Pattern: Parameterized Module Inclusion

+ + + +
+

I’ve run across a pattern in Ruby lately that I really like. It solves some problems that I’ve struggled with for several years. Let me start with the problems.

+

Let’s say you want to include an ORM in a model class, and want to tell it what database table to use. Typically, you’d do this:

+
class User
+  include MyORM::Model
+  table 'people'
+end
+

But that table call is more like an option to the module inclusion than anything else. So what we’d really like is something like this:

+
class User
+  include MyORM::Model, table: 'people'
+end
+

But that’s not valid Ruby; include doesn’t let you pass anything other than a module.

+

So when I was learning about Virtus, I noticed that its example of how to include it is a bit different than the standard Ruby idiomatic include:

+
class User
+  include Virtus.model
+end
+

At first glance, it reads like the first example. But on closer inspection and consideration, it’s quite a bit different. Where MyORM::Model is a constant that refers to a module, Virtus.model is a method call. So there’s a method named model in the Virtus module. That method returns another module — which is exactly what’s needed in order to include it into our model class.

+

The easiest way to implement Virtus.model would be this:

+
module Virtus
+  def model
+    ::Virtus::Model
+  end
+end
+
+module Virtus::Model
+  # ...
+end
+

If Virtus.model doesn’t need to take any arguments, that’s perfectly fine. In fact, I’ve started to use this implementation of the pattern for modules that don’t need parameters.

+

Because Virtus.model is a method, we can also call it with options:

+
class User
+  include Virtus.model(constructor: false, mass_assignment: false)
+end
+

We could even pass a block. But how do we process those options? There are a few different ways. However we do it, we have to be sure to return a module. And we can create modules in a few different ways.

+

Virtus uses the builder pattern. It takes the parameters passed in and builds a module dynamically. By that, I mean that it calls Module.new and then adds methods to that module. It does this by mixing in other modules, but it could do it by dynamically defining methods as well.

+

I’ve never seen this pattern in any other language. It’s obviously only possible because we can dynamically create modules.

+

The use of this idiom seems to be catching on a bit in the Ruby community. I’ve started using it myself, and will be adding it to my Includable::ActiveRecord gem soon.

+

 

+

 

+
+ + +
+ +
+
+ +

Brilliant – My Very Own Programming Language

+ + + +
+

I’ve decided to design and implement my own programming language, and call it Brilliant.

+

I’ve been interested in programming languages and linguistics almost as long as I’ve been using computers. I’ve long thought that if I ever go back to college, it’s likely that I’ll concentrate on programming languages as a specialty.

+

My recent discovery and involvement with the Crystal programming language has gotten me excited about new language ideas. It’s also helped me realize that implementing a language myself is feasible, and that there’s no better time to start than now.

+

Implementation

+

For now, the Brilliant implementation doesn’t let you do much more than “Hello World”. But you gotta start somewhere. So this article isn’t really “Introducing Brilliant” so much as the start of a series of articles on its design and implementation.

+

I wanted to use a PEG (parsing expression grammar) parser for several reasons. For one, they seem to have gained a lot of popularity in the past few years. Also, PEGs cannot be ambiguous, which can solve a few difficult problems, such as the “dangling else“. Perhaps my favorite feature of PEG grammars is that you don’t need a separate tokenizer (lexer). This provides a nice advantage in that we can use keywords like “class” as variables, as long as they’re not used in a place where the keyword would make sense.

+

So knowing that I wanted to use a PEG, I had to find a PEG parser. I kind of wanted to use ANTLR, which has been a leading parser for many years. But the PEG seems to be new to version 4, and I couldn’t find any Ruby bindings for version 4. Treetop seems to be the most popular parser for Ruby, but I found the EBNF format that Rattler uses to be more to my taste. I think the fact that it’s newer also gives it a few advantages, having had a chance to learn some lessons from Treetop.

+

I thought about using the Rubinius VM, but decided to go with LLVM, mainly since it has slightly better docs for Ruby, and because it’s what Crystal uses. Also, it’s pretty easy to get it to compile to a binary executable or run in a JIT. In the future, I might consider switching to the Rubinius VM, the Erlang VM, or the Perl 6 VM (Parrot). But for now, I like the idea of being able to compile to a binary and easily interface with C, just like Crystal.

+

Goals

+

My main goal is to have fun playing around with language ideas.

+

I’ve found a really great language in Ruby, so I’ll be using it as a starting point. But Ruby does have its faults. In some ways, I want to answer the question “what would Ruby look like if we designed it today?”.

+

But I also want to explore other ideas. What if objects defaulted to immutable? What if functions and methods were assumed to be pure by default? Might it be possible to infer the purity of a function or method? (If so, we could automatically memoize them.) Can we make creating an actor as easy as creating an object?

+

I’ll also be looking at ideas from other programming languages. Could we get some of the benefits of Haskell’s purity without having to do somersaults to do IO? Could we mix Python’s indentation style scoping with brace style or begin/end style? Could we integrate Icon’s ideas about success and failure (goal-directed execution)? What interesting features can we pull from Ada, Io, CoffeeScript, Crystal, Rubinius, Perl 6, etc.?
+

+

I’m not so much as interested in cutting-edge features, as in features that can be easily used by the average programmer. More importantly, I’m interested in how features interact with each other, so they fit well together to create a whole that’s greater than the sum of the parts.

+

Naming

+

I wanted to name my programming language “Fermat”. I’ve long been intrigued by Fermat’s Last Theorem, and Fermat’s Little Theorem is important in number theory. Unfortunately, there’s already a computer algebra system with that name.

+

So I decided to find a name in the same vein as “Ruby” and “Crystal”. I looked at the Wikipedia page for “gemstones” for some inspiration. A lot of the names of gemstones are already taken. I considered some obscure gemstones, but saw the word “brilliant” and thought it was decent. It’s not the name of another gemstone, but still evokes some similarity to Ruby and Crystal.

+

So that’s the name for now. Perhaps I’ll decide to change it at some point in the future, but I needed a name for the project, as well as a file extension for source code files. I chose “bril” for that. I suppose “br” would be a better choice. Perhaps I’ll change that before the next article in this series.

+

Future

+

I hope to work on Brilliant every once in a while. I expect it’ll take a couple years before it’s really very useful.

+

When I do add a major feature, I’ll be certain to blog about it. I’ve got tons of ideas strewn about in various files. It would be great to get them organized and published —and even better to get them implemented.

+
+ + +
+ +
+
+ +

Estimation Isn’t Agile

+ + + +
+

I don’t believe that estimation should be part of any Agile practice.

+

One of our managers recently mentioned that we hadn’t met the “contract” that we had “committed” to in our last iteration. This was complete nonsense, because A) we hadn’t made any such commitments, and B) we completed many more story points than the previous iterations (and without inflating story points).

+

estimates-as-deadlines

+

But her language made me come to several realizations. First and foremost, estimates are contracts. Sure, they’re not supposed to be treated as commitments, but they almost always are. And what does the Agile Manifesto say about this? It says that we should value customer collaboration over contract negotiation, and responding to change over following a plan. So it’s pretty clear that treating estimates as commitments is completely counter to the Agile values.

+

Why does this matter? What benefits do the Agile values bring us? I think the biggest benefit they bring is changing the way that we work, so that we can better deliver value to our customers. Without Agile, we’d just keep working the way we’ve always done things. And that didn’t seem to be working out so well. If we follow the Agile values and principles, at least we’ll have a fighting chance of improving our ability to deliver value.

+

Ask yourself — have you ever seen a software development project that was on time and on budget? Where the estimates were spot-on? Of course not. For one, we’re terrible at estimating. For another, our plans change — either from external factors, or from what we learn as we go.

+

Improved Estimation

+

To me, Agile is also about facing reality — and embracing it. It realizes that we’re terrible at estimating. It realizes that plans change. Most Agile methodologies have some tricks to counteract Hofstadter’s law. Generally, we use relative story points instead of hours, and then use an empirical factor to convert points to hours.

+

When this works, it is better than any other estimation I’ve ever seen. But it doesn’t work very often. People have trouble with relative estimation. How do you set the basis for what a point means without relating it to actual hours? Affinity estimation could work, but then you have to remember what the basis was. We’ve got a large distributed team, and when we tried this, we couldn’t all remember what the basis was.

+

Since we couldn’t get affinity estimation to work, we tried changing to perfect hours (only powers of 2). But then people thought of them as time. When we took longer than the estimate on an individual story, managers and team members thought we were taking longer than we should have. So our estimates ended up causing problems.

+

What Can We Do Instead?

+

Managers want estimates so that they can have predictability. They want to know when new features will be available. Is there a better way to get what we need?

+

I believe there’s a better way — prioritization. If you work on the most important thing first, then the most important thing will get done first. We should always be working on the next most important thing.

+

What if there’s more than 1 thing that’s most important? Then you’ve failed. You’ve failed at logic if you can’t understand that only 1 thing can be most important. You’ve failed at prioritizing the customers’ needs. You’ve failed at project management.

+

Arguments

+

1. Why can’t you just tell us how long it will really take?

+

Because we don’t know. Because we can’t know. This is the first time we’ve ever implemented the functionality you’ve asked for. If we’d done it before, we’d just use that existing code. As Glenn Vanderburg pointed out in his excellent talk on Software Engineering, we’re not building software, we’re architecting it.

+

2. But we have to tell our customers what to expect.

+

Why? Is the product so bad that you can’t keep customers around without leading them on with future enhancements? And why do customers need exact dates? A general roadmap telling them what the priorities for upcoming features should be sufficient.

+

3. But we have to have messaging about new features.

+

OK. Then send out that messaging once the feature has made it to Staging. Or even after it’s been rolled out to Production.

+

4. But we’ve promised these new features to the customers by this date.

+

Ah, so you’ve made promises to the customer that you don’t have control over. Have you ever heard of “under-promise and over-deliver”? That’s how you create happy customers. Yet you’ve done just the opposite, haven’t you? And then you want to blame someone else.

+

Risk

+

Estimates are risk. But the risk doesn’t come at the end, when the estimates are shown to be incorrect. The risk was in asking for the estimates in the first place, and placing trust in them. Don’t do it. Don’t promise things that you can’t be sure of.

+

Embrace this reality. Embrace this uncertainty. Always focus on what’s most important. That’s how you make customers happy.

+
+ + +
+ +
+
+ +

Slow Down!

+ + + +
+

There’s a tweet that I saw recently, with some simple advice for novice programmers:

+

Slow down.

+

This is probably good advice for most programmers. Our team recently noticed that every time we try to rush things, we make mistakes. And the mistakes end up costing us more time than if we had just done things at our normal pace. Slowing down ensures that you do things right, and when you do things right, you end up with a higher-quality product.

+

Speed and Code Quality

+

There are 2 types of code quality: internal and external. External code quality can be measured by how many bugs have been reported by customers. Internal code quality is harder to measure, but it mainly deals with the ability to change the code. When your internal quality is low, you’ve got lots of technical debt, and it’s harder to make changes.

+

So when you try to write code quickly, code quality decreases, leading to a code base that takes more time to make changes to. Conversely, when you slow down, your code quality improves, and it becomes easier to make changes more quickly. So when writing code, slowing down in the short run leads to a speed-up in the long run.

+

Speed and Process Improvement

+

But writing code isn’t the only place where we try to speed up. On an Agile team, we’re always trying to improve the way we work — especially at the beginning stages of an Agile transformation. So we’re eager to make changes in our processes. But I’d urge you to slow down here as well.

+

My colleague Amos and I frequently argue over pair switching. It’s funny, because we agree on everything except for 1 small detail. We both think pair switching is very important, to ensure that team members see more of what’s going on, to bring more ideas to each story, to prevent knowledge silos, and to encourage team ownership. Where we disagree is how long an ideal pairing session should last. I think pairs should switch every 2 hours, and he thinks 1 hour is ideal. I’ve seen teams reach the 1 hour pairing sessions successfully. But usually not without some pain and even often failing at the first attempt.

+

There’s nothing inherently wrong with failing. But if you fail at something, you’re not likely to try again. After all, you should learn from your failures, right?

+

So if you want your team to do something, you probably don’t want them to fail at it. If they fail, they won’t want to try a second time. That’s just human nature, and learning from failure. While you might think that they failed because they weren’t ready for the change yet, they’ll most likely think that they failed because this particular change won’t work for their situation. And they probably won’t know what to change when trying again, so they won’t try again.

+

I’ve seen this over and over. Back when Linux was up-and-coming, when a consultant pushed a company into using Linux before they were ready for it, and it didn’t work out, that company was cautious about trying again. So instead of being on the leading edge of using Linux, or even the middle of the pack, they ended up more toward the trailing edge. Had they not been pushed, they would have gotten more benefit in the long run.

+

So my advice in process improvement is the same as in programming: slow down. Take small steps toward what you think is the ideal. Make a small change, see how it works out, and adjust. As long as you’re still moving in the right direction, I believe you’ll move faster by taking small steps than by trying to make big leaps.

+
+ + +
+ +
+
+ +

Burying the Lede

+ + + +
+

Most of us don’t write very readable shell scripts. There are plenty of things we could do better, but today I want to talk about one in particular — burying the lede.

+

The term “burying the lede” comes from the field of journalism. Here’s the Wiktionary definition:

+

To begin a story with details of secondary importance to the reader while postponing more essential points or facts.

+

Like a good news article, code should tell a story. And the story should start with what’s most important. In the case of code, the most important information is the high-level functionality — a succinct summary of what the program does. In other words, write (and organize) the code top-down, as opposed to bottom-up.

+

Unfortunately, shell script doesn’t make this easy. Due to the way shell scripts are interpreted, you can’t call a function until after you’ve defined it. This leads to most of us structuring our code like this:

+
function do_something { ... }
+function do_something_else { ... }
+
+do_something
+do_something_else
+

The problem with this is that the function definitions will likely take quite a few lines, and we won’t see what the top-level functionality is until we reach the end of the script.

+

I’d like to propose a standard way to structure shell scripts to mitigate this issue. (I’m really only talking about shell scripts that have function definitions within them.) I’m sure I’ve seen a few scripts do this, but it’s not very common at all.

+

My proposal is simple:

+
function main {
+  do_something
+  do_something_else
+}
+function do_something { ... }
+function do_something_else { ... }
+
+main
+

This structure lets us start with the lede. We describe the top-level functionality right away. Only then do we get to the secondary details. The name main makes it pretty clear that it contains the top-level functionality.

+

I’ve recently started writing my shell code like this, and I’m happy with the results. I’ve also started to use some other programming techniques in my shell scripts to improve readability: better naming, extracting more methods, and moving helper methods into separate files. It feels good to treat shell scripts like real code instead of just some stuff I’ve hacked together.

+

PS. The WordPress theme I’m currently using (Twenty Eleven) also buries the lede — I can barely even see the title of the blog post on my screen without scrolling. I’m going to have to change that soon.

+
+ + +
+ +
+
+ +

Yak Shaving #1: Cursor Keys

+ + + +
+

I recently decided to start using Emacs again. I used it extensively from the early 1990s until the early 2000s. I pretty much stopped using it when I had a sysadmin job with no Emacs on the servers, and no ability to install it. With the rising popularity of tmux and tmate for remote pairing, and my dislike for vim’s modes, I decided to try going back to Emacs in the terminal.

+

One thing I really want in a text editor is good cursor key support. Shifted cursor keys
+should select text, and Control+left and right should move by words. (Apple HIG says to use Option+left and right to move by words; most apps on Mac OS X seem to support both.) Things have worked this way on almost every text editor on every OS I’ve used — Amiga, DOS, Windows, NeXT, Mac, Motif, Gnome, KDE. It’s a part of the CUA standard that’s been in common usage on everything since the mid-1980s.

+

Enabling cursor keys in Emacs was pretty easy. I’ve decided to use Prelude to make getting started with Emacs easy. Emacs comes with the cursor keys enabled, but Prelude disables them. Undoing Prelude’s change is pretty easy:

+
(setq prelude-guru nil)
+

Trying to make shifted cursor keys work is where the trouble began. They work in the GUI version of Emacs, but not from the terminal. It turns out that the Mac Terminal doesn’t distinguish between cursor keys and shifted cursor keys in its default configuration. So I had to figure out how to configure key bindings in Terminal.

+

That’s easy enough — they’re in the preferences. But what should I set them to? This took a lot of research. Terminal emulation and ANSI code sequences are obscure, complex, and inconsistent. I eventually found the info I needed. For starters, Shift+Right, Shift+Left, Shift+Home, and Shift+End are defined in the terminfo. The rest I was able to piece together from various sources around the Internet.

+

I’m also trying to script my Mac configuration. So instead of manually adding all the keybindings in the Terminal preferences pane, I decided to write a script. Mac OS X does a decent job of allowing you to change preferences from the command line. For example, to always show the tab bar:

+
defaults write -app Terminal ShowTabBar 1
+

Easy enough, except for a couple problems. First, I had to figure out the obscure syntax used in the preferences for key codes. I was able to piece these together with some more Internet research. But the really big problem is that the keyboard bindings are 2 levels deep within a “dictionary” (hash map). And the defaults command doesn’t handle that. There are some obscure utilities that handle nested preferences, but they don’t work well with the preferences caching in Mac OS X 10.9 — a problem I ran into while testing.

+

So now I’m writing a utility in Python that does what the defaults command does, but that will handle nested dictionaries.

+

There’s a term for this process of having to solve one issue before you can solve another, and the issues get several layers deep. It’s called yak shaving.

+

Here’s another good example:

+
Father from Malcolm in the middle having to fix one thing before fixing another, ad infinitum.
Yak shaving – home repairs
+

I’m sure this will be the first of many posts about me yak shaving.

+
+ + +
+ +
+
+ +

Chording Keyers

+ + + +
+

I’m considering buying a chording keyer. A keyer is a 1-handed text input device. Chording means that you can hit more than 1 key at a time. I’ve been interested in these for a long time actually. They’ve been popular in the wearable computing (“cyborg”) field, but never caught on anywhere else. I’ve always thought that 1-handed chording keyer would be a great input device to use with a cell phone. It’d be even better with a heads-up display like the Google Glass.

+

There are quite a few chording keyers available. It doesn’t appear that any will meet exactly what I’m really looking for, but I might take the leap anyway. It should be a hand-held device. I really want a Bluetooth device, so I can use it with a computer, phone, or tablet. Below are the ones I’m considering.

+

Twiddler

+

The Twiddler has been around quite a while, as probably the main commercially available keyer for wearable computing. There was a time that it was unavailable, but there’s a new version available from HandyKey now. It’s $200 ($220 Canadian), and comes in only USB.

+

The Twiddler looks like a small TV remote. The current version (2.1) has 4 thumb keys, a thumb joystick (for mouse), and 12 small finger keys. It has good community support, including an alternate chord layout.

+

In10did Decatxt

+

In10did has been trying to bring out a few variations of their devices. The Decatxt looks like a pack of cigarettes, with 8 keys on the front and 2 thumb keys. It can be used with 1 or 2 hands. All letters can be typed with 1 finger or 1 thumb and 1 finger.

+

The Decatxt has USB and Bluetooth models, but I’m unable to find pricing or a way to buy one. It looks like there have been no updates for over a year, so I’m concerned that this is going to be vaporware.

+

CyKey

+

The CyKey is based on the Microwriter, which goes back to the early 1980s. The Microwriter was a desktop-based device, but it looks like the CyKey might be able to be used as a hand-held device. It has 9 buttons on the face, arranged in 3 groups of 3 buttons.

+

The CyKey is $125 (£75) and works over InfraRed connected to USB. A Bluetooth model is supposedly in the works.

+

EkaPad

+

The EkaPad is like a cross between the Decatxt and the Twiddler. It has 12 buttons on the face, but no thumb keys. It’s got a nice well-thought-out design that looks quite ergonomic.

+

The EkaPad is $150, available in USB only. A desktop holder is available to hold it upright, or it can be used hand-held.

+

FrogPad

+

The FrogPad is meant to be used on a flat surface only. It looks kind of like a third of a laptop keyboard. It’s got 15 regular keys and 5 modifier keys. Only 1 key and 1 modifier can be chorded. Right-handed and left-handed models are available, with USB connectivity.

+

The FrogPad2 was recently announced, replacing the original FrogPad. It switches to standard desktop-style keys, and adds some more keys along the top. It supports USB On-the-Go and Bluetooth. I’m not really interested in this model with its regular keys, but it doesn’t appear that the original version is being made any longer. The new models are priced at $200. I believe the old models were $150.

+

Chordite

+

The Chordite looks the most promising, but it’s only a concept with some DIY prototypes. It’s a hand-held device, with 2 buttons on each of the 4 fingers — one activated by the distal phalanx and the other by the middle phalanx. This initially sounds like it would be difficult to press 2 different buttons with 1 finger, but I think the way they’re positioned would make it actually work really well.

+

Septambic Keyer

+

The septambic keyer is another DIY concept, hand-built and used by wearable computing folks. It’s fitted to your palm, and you squeeze your fingers toward a fist to activate the buttons. There’s one button for each of the 4 fingers, and 3 for the thumb.

+

Clove 2

+

The Clove 2 is a Bluetooth-enabled glove with 5 buttons — 1 for each finger. It’s another do-it-yourself project.

+

Software Options

+

I found an interesting project called ASETNIOP that allows chording on a standard Qwerty keyboard. (Assuming the keyboard supports multi-key roll-over well enough. Most keyboards should work.) It’s got a really nice online interactive tutorial. You basically chord with the 8 home-row keys, plus the spacebar.

+

For Android, a program called GKOS for Android uses multi-touch with 1 or 2 button presses. The buttons are different sizes, depending on their frequency. I like this concept, but I don’t think the implementation is quite ready for production.

+

I also found an app for iOS called Nintype that is a combination between Swype and a chording keyer.

+

Conclusion

+

I think I’m going to try the Twiddler. If I find the concept to be a good one, hopefully I’ll build myself a Chordite (or have someone build one for me).

+

I’m also going to play with implementing some of the ideas from ASETNIOP, using KeyRemap4MacBook.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/page/4.html b/public/page/4.html new file mode 100644 index 0000000..819b06a --- /dev/null +++ b/public/page/4.html @@ -0,0 +1,752 @@ + + + + + + + + + + + + + + + +BoochTek, LLC – Page 4 – Web Development, Ruby on Rails, Open Source + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + +
+
+ +

Empathy

+ + + +
+

I facilitated our team retrospective this morning. I felt like we made a little forward progress, but not as much as I would have liked. But it really brought one thing to the forefront of my thoughts today — empathy gained through communication.

+

We have a pretty large team by Agile standards — we had 20 people in our retro: 16 developers, 3 QA folks, and 1 manager. Out of those, only about 5 or 6 speak up regularly. I recently sent out a survey to the team, trying to get feedback on how we could improve our retros. A couple of the questions tried to get a feel for why people aren’t speaking up more. Only about half the people responded, and the answers didn’t really answer my question as well as I had hoped.

+

So on Amos‘s suggestion, we did the Safety Check exercise. We got a good set of answers to why people don’t feel safe. About half of the answers were about the fear of looking stupid in front of other people. About half of those mentioned the manager — they’re worried they might get in trouble for what they say. We talked some about fear and how it’s more often than not misplaced. And that the worst consequences are usually not as bad as you might think. But then we got to the crux of the problem — there’s not enough trust amongst the team, and especially between the team members and the manager.

+

About half of our team is new (within the past 6 months) — including the manager. While the developers have made some good progress building trust amongst each other, we haven’t had as much time with the manager to build trust between him and the rest of the team. So the lack of trust isn’t at all surprising.

+

Honestly, I already knew we had trust issues, and wanted to address them, but needed a way to lead the team to that same realization. With this exercise driving out the issue, we were then able to have a conversation about trust. The conversation was pretty good. We got more voices to contribute than probably any other topic we’d previously covered. (I was disappointed that the manager kept quiet though. I later found that he was trying to mitigate people’s fears by keeping quiet, but I urged him to contribute more in the future.)

+

But one point really stood out in my mind — a point of view that I hadn’t previously thought much about. Lauren, one of our QA analysts, pointed out that most human communication is non-verbal. We give tons of cues via body language, facial expressions, eye contact, tone of voice, even posture. I don’t recall if Lauren said it explicitly, but she pointed out that these cues build empathy between the speakers. She encouraged us to use more voice chat and video chat, as opposed to IRC text chat, because it would create more empathy between the people communicating, which would lead to more trust.

+

I spent most of the rest of the day talking to people on the phone or via Google Hangouts voice. And every single time, I consciously noticed that I was gaining empathy for the person I was speaking with. I assume (and hope) that that’s working both ways. I suppose that it’s always been happening, but I never really noticed it.

+

I’ve heard a lot of talk about empathy among Agile practitioners lately. It’s been mentioned on the Ruby Rogues podcast, and Angela Harms has been preaching it for years. I already understood how important it is. But until today, I didn’t really feel it happening.

+

So if you’re looking to build trust with someone, spend some time talking with them. Preferably in person, but if that’s not possible, seriously consider video or voice modes of communication, instead of sending an email or an instant message.

+

 

+
+ + +
+ +
+
+ +

What I Want in a Blog Engine

+ + + +
+

I’m considering moving away from WordPress, for a couple reasons:

+
    +
  • Security. There have been several vulnerabilities over the past few years, and I’ve never really had a high level of confidence that it’s secure. In addition, I now find the whole PHP model — every web app running as a single user, instead of leveraging UNIX permissions — to be broken.
  • +
  • Speed. I started to realize a couple years ago that a blog engine should generate static pages. The static pages should be updated whenever new content is added. There’s really no reason to re-generate the page every time someone wants to read it. At the very least, Server-Side Includes (SSI) or Edge-Side Includes (ESI) should be used.
  • +
+

Now that I’m starting to actually blog consistently, it makes sense to change. But before I move to something different, I want to be sure that I find all the features that I need. This post is my thought process about what I want.

+

Requirements

+
    +
  • Self hosted. I like to run my own servers. Partly so I can keep full control, and partly because I enjoy doing some system admin stuff (as long as it doesn’t take too much time).
  • +
  • Generation of static pages. Almost everything should be a static page by the time a user requests a page. Obviously, submitting comments would hit a dynamic page, and comment approval would likely be done through a dynamic part of the site. But publishing a blog post or comments should generate static files to be served by Apache or nginx.
  • +
  • Categories (or maybe tags). I want readers to be able to find other things that I’ve written that might interest them. A list of categories in a sidebar would help them find what interests them.
  • +
  • Site map. Especially an XML sitemap, so Google can index better. I also like the idea of readers being able to see everything in one place.
  • +
  • Archives. This is another way of letting readers see everything I’ve written, organized by publication date.
  • +
  • RSS and/or Atom. I want readers to be able to subscribe to my blog.
  • +
  • Media. I don’t think I necessarily need a media manager, just a simple way to add pictures to blog posts.
  • +
  • Comments. I want to allow comments, but I don’t want to use anything like Disqus. I want the comments to be in my own database, so they won’t disappear. I also dislike requiring JavaScript just to see comments. And I’ve worked at places where Disqus wouldn’t work, due to web proxy servers. Comments should be moderated by the site admin, editor, or author. Readers should not have to log in to post a comment, but it would be nice to allow users to log into Twitter or some other social network to provide their info.
  • +
  • Pingbacks and Tweetbacks. I’d like to be able to see who’s referring to my articles.
  • +
+

Nice to Have

+
    +
  • Free. I almost consider this to be a requirement, but I’d be willing to entertain a 1-time payment for something that meets my needs really well. I need to try to counteract my DIY-at-all-non-monetary-costs mentality.
  • +
  • Open Source. I think Open Source is likely to provide a more useful product, especially in this space. An Open Source option is more likely to improve over time.
  • +
  • Not PHP. I’ve got several problems with PHP. First, the whole PHP model of running in the same context as Apache (or nginx) breaks UNIX conventions. A security issue in a PHP program exposes more than just the program itself — it exposes Apache and every other PHP program. Under the UNIX model, each program runs in a different user context, isolating bugs to that program only. PHP is also a “hacky” language. PHP programmers tend to not be as rigorous (in many ways), leading to more vulnerabilities. Finally, the PHP language itself has had several misfeatures that have lead to security issues — much more than Ruby or Java.
  • +
  • Not MySQL. I’d like to be able to remove MySQL from my servers. PostgreSQL would be much preferred. Storing everything in git would be fine too — maybe even preferred. I’d even be fine with MongoDB or some other simple NoSQL database.
  • +
  • Admin console. I can’t imagine approving comments without this, but I’m not closed to that possibility.
  • +
  • Edit history. I want to be able to update posts, but see what the previous versions looked like. (Not sure if I care whether the public can or cannot see the various revisions.)
  • +
  • WYSIWYG. I’d be willing to settle for Markdown. In fact, I’d prefer (extended) Markdown as the canonical form behind the WYSIWYG.
  • +
  • Code highlighting. A lot of my blog posts (will) include code in various languages. I’d like the blogging software to be able to handle that well. Syntax highlighting would be great. Any other related features would also be nice.
  • +
  • Through-the-web editing. I’d like to be able to edit a page from any device from anywhere that has Internet access.
  • +
  • Reader-submitted edits. This is probably going to be the hardest thing to find, but I’d like a really easy way for readers to let me know of typos and such by simply submitting a fix for them. The author or editor of the post would have to approve the edits, obviously.
  • +
  • Import from WordPress. I don’t have a ton of content in WordPress, or else this would probably be a more firm requirement.
  • +
  • Community. Community helps software thrive, adding improvements and ensuring security.
  • +
  • Plugins. It would be nice for the blog engine to be extensible.
  • +
  • Unpublished drafts and previews. I’d really like to be able to see by blog posts before I release them to the world.
  • +
  • CMS abilities. I’d really like the ability to edit non-blog web pages the same way as blog entries.
  • +
  • Themes. It would be nice to have decently looking themes, so I could have other people help me make the site look nice.
  • +
  • Posting my tweets in a sidebar area. I’d be OK with this being done in client-side JavaScript. I’d also be OK with periodic polling of my Twitter feed, and updating the static content when appropriate.
  • +
+

Candidates

+

I’ve been playing with Jekyll lately, using it to generate a simple static site that can be updated easily. It’s pretty nice, and pretty easy to use. Something based on Jekyll would work well. The trickiest part will be figuring out how to manage comments. Or maybe I’ll just have to learn to live more minimally.

+

Octopress is the obvious candidate. It fits most of my needs and wants. Most likely I’ll write my own commenting engine and add it to my Octopress template.

+

The only other candidate that I’ve found is Ghost. It’s written in JavaScript and runs on Node. I don’t find Node to be that compelling, but the templates look great, and it’s designed for coding blogs.

+

I’ll probably take a quick look at Droplets, too. It’s written in PHP, but seems to fit my other requirements.

+

Unfortunately, that seems to be all there is in this space. Maybe I need to investigate more. Or maybe Octopress just has the static blog generation marked cornered. Either way, I’ll probably change my blogging software in the next month or two. Wish me luck!

+

 

+
+ + +
+ +
+
+ +

Testing Rails Validators

+ + + +
+

It’s challenging to test Rails custom validators.

+

I recently had to write a validator to require that an entered date is before or after a specified date.

+

It didn’t seem like writing the validator would be too difficult – I’ve written custom validators before, and date comparisons aren’t all that tricky. But when it came time to write the tests, I ran into several issues. And since I always try to follow TDD / test-first, I was blocked before I even began.

+

The biggest issue was the ActiveModel::EachValidator#validates_each API. It’s definitely not a well-designed API. You write your validator as a subclass, overriding validates_each. The method takes a model object, the name of the attribute of the model being tested, and the value of that attribute. You can also get the options passed to the custom validator via the options method. To perform a validation, you have to update the model’s errors hash.

+

The big flaw in the API is that instead of returning a result, you have to update the model. This needlessly couples the model and the validator. And it violates the Single Responsibility Principle — it has to determine validity of the field, and it has to update the errors hash of the model. This is not conducive to testing. Testing this method requires testing that the side-effect has taken place in the collaborator (model), which means it’s not really a unit test any more.

+

So to make it easier to unit test the validator, I broke the coupling by breaking it into 2 pieces, one for each responsibility. I moved the responsibility for determining validity to a separate method, which I called errors_for. It returns a hash of the errors found. This simplified the validates_each method to simply take the result of errors_for and update the errors hash of the model:

+
def validate_each(record, attribute_name, attribute_value)
+  record.errors[attribute_name].concat(errors_for(attribute_value, options))
+end
+

This made it much easier to unit test the errors_for method. This method doesn’t even need to know about the model — only about the value of the attribute we’re trying to validate. We simply pass in the attribute’s value and the options.

+

So we could write the tests without even pulling in ActiveRecord or any models:

+
describe DateValidator do
+  let(:validator) { DateValidator.new(attributes: :attribute_name) }
+  let(:errors) { validator.errors_for(attribute_value, validation_options) }
+
+  describe 'when attribute value is NOT a valid date' do
+    let(:attribute_value) { 'not a valid date' }
+    it { errors.must_include 'is not a valid date' }
+  end
+
+  describe 'when attribute value IS a valid date' do
+    let(:attribute_value) { Date.parse('2013-12-11') }
+    it { errors.must_be :empty? }
+  end
+end
+

And the errors_for method looked something like this:

+
def errors_for(attribute_value, options)
+  unless attribute_value.is_a?(Date)
+    return [options.fetch(:message, "is not a valid date")]
+  end
+  []
+end
+

Integration testing can also be a bit of a challenge. I recommend following the example from this Stack Overflow answer. Basically, create a minimal model object that contains the field and the validation. Then test that the model behaves like you expect with different values and validations.

+
+ + +
+ +
+
+ +

Introspective

+ + + +
+

Today I was informed that I’ve been impeding progress on our team.
+This was somewhat shocking, since I feel like I’m always pushing
+forward to make our team and myself better.

+

Like most anyone, my initial reaction to criticism was defensiveness.
+I don’t handle criticism well. (You might find that hard to believe,
+after reading the rest of this post. Maybe it’s just the confrontation
+part I don’t handle well.) Thankfully the blow was softened somewhat,
+because the person providing the criticism didn’t tell me directly —
+they told Amos, a trusted colleague of mine. Amos then passed it on to
+me. I’m grateful for that — this is the best way for me to have received
+that criticism.

+

What I did next is the astonishing thing. I swallowed my pride for a
+minute. Not an easy thing for me to do, for all the usual reasons, and
+more. I decided to address the problem head on. If someone was telling
+someone else that I was causing some sort of a problem, then even that
+perception was a problem that I needed to address. So I decided to hold
+a retrospective for myself. Retrospectives are the way Agile teams address
+problems and improve how they work. If it would work for a team, I figured
+it should work for an individual.

+

I’d held retrospectives by myself before. Before I even knew about
+retrospectives, I’d taken some time to assess how I had done things
+on a couple of projects I had worked on as an independent contractor. But
+those were about technical decisions I had made and processes I had
+followed. This one would be different. This one was about a personal
+shortcoming, an action of mine that had caused some harm. This one
+involved feelings and emotions.

+

So I asked Amos to facilitate my one-person retro. We’d both done a lot
+of retrospectives, but neither one of us had done anything like this before.
+We had a bit of trouble getting started. We didn’t have any details
+about why the anonymous person felt the way he felt. So it was hard
+to address something that nebulous. So I asked Amos if he could follow
+up with the person. Luckily, the person was online and able to respond
+in real time.

+

So we talked things through some. We went on some tangents. We made some
+progress — things that I could write down as take-aways and action items.
+We got off-topic again, and then came back to it. Then Amos decided to
+play The Why Game. This worked amazingly well. It helped me to find the
+root reasons why I was holding back change. Basically I’m afraid of
+failure. Going deeper, we looked into why I’m afraid of failure. This
+line of inquiry was particularly effective. Hopefully it’ll be enough for
+me to stop being an impediment to change. Here are my notes / take-aways:

+
    +
  • Be careful playing devil’s advocate in public. +
      +
    • People will think I’m taking that stance.
    • +
    +
  • +
  • Don’t always take the boss’s side in public.
  • +
  • Don’t be the person to allow the team to take the easy path.
  • +
  • Why do I think that managers won’t let us try a 2nd time?
  • +
  • Why do I think we might fail the first time? +
      +
    • I want to put us in a better position to succeed.
    • +
    +
  • +
  • I’m being too conservative.
  • +
  • Be willing to fail. Don’t fail to try.
  • +
  • Don’t say we shouldn’t try because I think other people don’t want to try. +
      +
    • That is just self-perpetuating.
    • +
    +
  • +
  • I’m afraid of failing because I haven’t done it much. +
      +
    • Because I’m good at learning from other people’s failures.
    • +
    • But this is holding me back and making me cautious. +
        +
      • Despite the fact that I try to not be cautious.
      • +
      +
    • +
    • So I need to work to counter-act those fears.
    • +
    +
  • +
+

At first, I thought that I’d never heard of anyone doing anything like
+this. But then I recalled that I’d heard about somewhere that everyone
+does this, with their whole team. That would be hard. I’d have to
+trust everyone in the room.

+

But I would recommend this for anyone that can pull it off. It’s hard,
+but it has a large payoff. Just like a team retrospective, it feels good
+making this kind of breakthrough, and knowing that just spending that
+little amount of time has made me a better person. (Mine took about an hour.)
+I think the hardest part is finding someone (or a group of people) you
+trust to be your facilitator.

+

I’ve decided to call this thing an introspective. A retrospective is
+about looking back. This is about looking inward. I’d be interested to
+find out who is doing this kind of thing. Does it have a commonly accepted
+name? How does it work? What techniques work best? If you’ve got any answers or ideas, please comment below, or tweet me @CraigBuchek.

+

So thank you to that anonymous person. Your way of addressing this was
+probably more effective than you could have imagined that it might be.
+I hope that this exercise will help me make the changes required, to make
+me a better person, and help the team become the best that we can be.

+

Amos has written up his thoughts on the introspective.

+
+ + +
+ +
+
+ +

What’s Your Open Source Resolution?

+ + + +
+

I’ve been using GNU/Linux since 1994, starting with a Slackware 2.2 CD-ROM I ordered from the back of a magazine. I’ve been heavily involved in my local GNU/Linux community since 1999, and I give frequent presentations at various groups. I’ve made some small contributions to several Open Source projects.

+

But I don’t feel like I’ve done enough to contribute to the wider community. So this year, I’m going to resolve to step up my contributions, and do more.

+

Change my role in the LUG

+

I was lucky enough this past year to find someone interested in helping to run the St. Louis GNU/Linux Users Group (STLLUG). I’ve been running the group so long that I’ve somewhat stagnated. It’s great to find someone who will bring a fresh energy and new ideas. Thanks, Andrew!

+

Mostly freed from the more general administrative tasks (mostly planning meeting topics and speakers), I’m hoping to find the time to work on a few projects. First, I want to overhaul the web site. It needs a new look, as well as an easier way to update it. I’m planning to implement some sort of CMS — possibly WordPress or Octopress, or at the very least, a git repo to manage updates.

+

Blog regularly

+

I installed WordPress a long time ago, but I haven’t published many posts. I’ve got a few drafts in various stages. So this year I’m going to actually get the blog going, and hopefully post about once a week. I’ll be sharing things I learn on the Internet, as well as some of the things I learn from my experiences with various technologies and techniques.

+

Contribute more significant patches

+

There are a few items I’ve been meaning to work on and contribute. I’d like to finally get around to them:

+
    +
  • Make the GNU sort command be able to (easily) sort a list of IP addresses, or semantic version numbers.
  • +
  • Add MySQL-style commands to PostgreSQL: SHOW DATABASES; SHOW TABLES; DESCRIBE table. (I’m not sure how welcome these additions might be, but I think they’d make transitioning from MySQL easier for people.)
  • +
+

Publish and maintain several Ruby gems

+

I’ve been working on a few Ruby gems. I’d like to get them to a state where they’re useful to more people. These include:

+
    +
  • A way to simplify standard CRUD controller actions
  • +
  • A way to declare list views in a manner similar to SimpleForm or Formtastic
  • +
  • A way to declare ActiveRecord attributes in models using Virtus
  • +
  • A higher-level interface to ROM (Ruby Object Mapper)
  • +
  • A similar high-level way to define Perpetuity attributes in models using Virtus
  • +
  • My Rails template, making it much easier to start a Rails app
  • +
+

Build some apps

+

I enjoyed building a quick Rails app during last year’s Rails Rumble, even if it doesn’t turn out to be useful or make any money. I’d like to create a few small apps again this year, either in Rails or Apache Cordova. I’ve got some ideas — we’ll see how if they turn into anything useful or worth selling.

+

Podcast more often

+

Last year, I started joining the This Agile Life podcast. I enjoy it. I’m hoping to continue with that, and possibly doing some podcasting about Ruby and Rails.

+

Present at a conference

+

I’m going to attempt to give a presentation at a conference. I often give presentations at local groups, but haven’t addressed a larger audience. I’ve got a few ideas rattling around, and will see if I can turn them into presentations worthy of a larger conference.

+

What’s your Open Source resolution?

+
+ + +
+ +
+
+ +

My Thoughts on Python vs. Ruby

+ + + +
+

I’ve been using Python at work for the past few months.  I learned Python back in the early 2000s, but never used it for any large projects.  I learned Ruby in late 2005, and it quickly became my language of choice for most cases.

+

So while I still prefer Ruby, and will likely use Ruby more in the future than Python, I wanted to assess the strengths and weaknesses of Python in relation to Ruby.  Perhaps some of the lessons could be applied when writing Ruby, and it could help to decide when to use each.  Also, I’m interested in programming language design, and wanted to document pros and cons in that light.

+

Where Python Sucks (As Compared to Ruby)

+
    +
  • Have to explicitly include self in EVERY method declaration. +
      +
    • Including @classmethod declarations (although people usually use the name cls instead of self there).
    • +
    • Except @staticmethod declarations.
    • +
    +
  • +
  • Have to use self everywhere to reference an object’s own attributes.
  • +
  • Inconsistency of things like the len() function, when everything else is a method.
  • +
  • Inconsistency of having some built-in classes with lower-case names. +
      +
    • And they’re not whole words, so you have to memorize them: list, dict, str, int.
    • +
    • Even more bizarre is dict vs. OrderedDict.
    • +
    +
  • +
  • I’m not a fan of True and False being uppercase. +
      +
    • I’m a bit less concerned about None, for some reason.
    • +
    • If they’re going to be uppercase, they might as well be all caps, in my opinion.
    • +
    +
  • +
  • Inconsistency of camelCase versus snake_case in some modules.
  • +
  • Claims to prefer explicit over implicit, yet does not use new to create an object. +
      +
    • It is nicely concise, but makes it look too much like a regular function call.
    • +
    +
  • +
  • The implementation of lambdas is too limited. +
      +
    • Makes using map function not very useful.
    • +
    +
  • +
  • Class methods are less than ideal to implement. +
      +
    • Probably better to use functions within a module instead, in many cases.
    • +
    +
  • +
  • Lack of good functional programming tools makes it harder to manipulate lists. +
      +
    • Have to often resort to creating an initial list and adding to it in a for loop.
    • +
    +
  • +
  • I don’t understand why r’regex_string’ doesn’t just create an actual regular expression object.
  • +
  • I miss Ruby’s method-call-or-property-getter syntax. +
      +
    • Nice that I can get it by adding @property to method definitions, but that’s a bit messy.
    • +
    +
  • +
  • I don’t understand why lists don’t have a join() method; it seems backwards to call join on the string used to connect the list elements.
  • +
  • I miss unless; seems like with all the keywords, Python would have added that.
  • +
  • I really miss ||= to memoize. +
      +
    • The distinction between unset variables and variables set to None makes it hard.
    • +
    • I also miss +=.
    • +
    +
  • +
  • I really miss statement modifiers (if or unless at the end of the statement/expression instead of the beginning).
  • +
  • I miss being able to assign to a compound statement (x = if True: 1; else: 2). +
      +
    • While Python does allow this simple case, it does not allow more complex cases.
    • +
    +
  • +
  • The way modules, files, and classes work, I either have a lot of classes in one file, or have to come up with more module names in order to split classes into different files.
  • +
  • The preference for bare functions within modules over class methods often leads to functions outside of classes, when they’d make more sense more closely associated with the class.
  • +
  • I don’t quite understand the necessity for pass. +
      +
    • Seems like allowing 0 lines of indented code would suffice instead.
    • +
    +
  • +
  • I miss implicit return. +
      +
    • Explicit return looks a bit nicer, but is less concise, and I’ve gotten out of the habit.
    • +
    +
  • +
  • Mixins turn out to be more useful than multiple inheritance.
  • +
  • I miss string interpolation.
  • +
  • I really miss Array#first and Array#last.
  • +
  • I really don’t like that 0 is falsey. +
      +
    • I could live with empty lists and dicts being false, but not 0.0 and 0.
    • +
    +
  • +
  • Comparing a string to a regular expression seems harder than it should be.
  • +
  • Converting to a Boolean is not as easy a Ruby’s !! syntax. +
      +
    • OK, bool(expression) isn’t so bad, I guess.
    • +
    +
  • +
+

Where Python Rocks

+
    +
  • Indentation removes the need for end everywhere.
  • +
  • Import statements are nice. +
      +
    • Can import everything, or just a few things.
    • +
    +
  • +
  • Annotations are nice for aspect-oriented modification of methods. +
      +
    • Does it make it hard to debug though, like alias_method_chain in Ruby?
    • +
    • It’s kind of tricky to write them though, due to doubly-nested function definitions.
    • +
    +
  • +
  • List comprehensions are more powerful than map. +
      +
    • But map can be more concise for the most common cases.
    • +
    +
  • +
  • Can add arbitrary attributes to any object, class, or module.
  • +
  • The object creation syntax is nicely concise.
  • +
  • Sometimes the ability to add a bare function within a module is nice.
  • +
  • Keyword arguments are nicely done. +
      +
    • Ruby’s emulation via hashes without braces is OK, but the corner cases are problematic.
    • +
    +
  • +
  • Docstrings as part of the language is nice.
  • +
  • The new with keyword looks like a halfway-decent replacement for blocks. +
      +
    • Seems like a lot more work that using blocks and yield though.
    • +
    +
  • +
  • Python 3 drops support for octal literals starting with a 0. +
      +
    • Still allows it via a 0o prefix.
    • +
    +
  • +
  • No support for all the crazy Perl-inspired globals.
  • +
  • The names list and dictionary are better than array and hash. +
      +
    • The Ruby names are more about the implementation, especially hash.
    • +
    • I’d much prefer the name map over hash, or even dictionary.
    • +
    • The name list is only slightly better than array.
    • +
    +
  • +
  • List and string slicing is quite nice. +
      +
    • I do wish that the syntax was “..” instead of “:” though.
    • +
    • Slice assignment is even cooler.
    • +
    +
  • +
  • I prefer the Python dict syntax over the Ruby hash syntax (“:” vs. “=>”). +
      +
    • The Ruby 1.9 symbol hash syntax is an improvement, but not quite as good.
    • +
    +
  • +
  • Checking for string containment is nice: if substring in string.
  • +
+

Where Ruby Rocks

+
    +
  • Consistency.
  • +
  • Blocks.
  • +
  • Excellent functional tools to deal with Enumerable.
  • +
  • Meta-programming.
  • +
  • Optional parentheses.
  • +
  • Modules and classes are also objects.
  • +
+

 

+
+ + +
+ +
+
+ +

Debugging Pattern – Grenade

+ + + +
+

I’ve been more interested in programming patterns lately, partly due to the Ruby community’s recent interest — especially the Ruby Rogue podcast’s love affair with “Smalltalk Best Practice Patterns”.

+

I consider most of the patterns in “Smalltalk Best Practice Patterns” to be patterns “in the small” — things that are typically employed on a single line or method. The Gang Of Four patterns are more medium sized, dealing with methods and classes. The PEAA book covers architectural-scale patterns. I suppose “The Pragmatic Programmer” and similar books could (should!) be considered to be very general patterns, mostly about the practice of programming.

+

One type of pattern that I have not seen discussed much is debugging patterns. I’m not exactly sure why that is; probably just our general concentration on designing the program code itself. There are definitely testing patterns. But I can’t think of anything that I’ve seen that covers debugging patterns. A quick search on the Internet doesn’t turn up too much.

+

Anyway, a co-worker (Helena) and I were debugging recently, and were having trouble figuring out where a certain method on a certain object was being called. We came up with a really cool solution. We immediately called it a grenade, because its entire purpose is to blow up (raise an exception) and display a backtrace to show us the caller of the method that we’re looking for.

+

Here’s our implementation in Ruby:

+

+module Grenade
+  def method_under_investigation(*)
+    raise "method_under_investigation called"
+  end
+end
+
+class Xyz
+  ...
+  def xyz
+    object_under_investigation.extend(Grenade)
+  end
+  ...
+end
+

I’m sure we could wrap that in some more semantic sugar, even going as far as making it look something like this:

+

+class Xyz
+  ...
+  def xyz
+    object_under_investigation.blow_up_when_method_called(:method_under_investigation)
+  end
+  ...
+end
+
+

I’m not sure that would really be worth it though, unless we were to add it to some sort of library.

+

So that’s our contribution to the (small) list of debugging patterns.

+
+ + +
+ +
+
+ +

Write Comments For Yourself

+ + + +
+

Amos and I got in a heated discussion recently on whether we should write a single line comment to better explain some code. (The code in question was Amos’s very elegant solution to testing whether a job got sent to Resque.)

+

Amos doesn’t believe in writing comments much at all. He thinks that if you’re writing a comment, it means that you’re doing something wrong, and that you probably need to write the code more clearly.

+

I agree with that, to a point. First off, it’s not necessary to write perfect code. If you can change a class or method name to better describe what you’re doing (and more importantly, why you’re doing it) then you should definitely do so. But it’s not always worth refactoring until you get every “why” answered. More importantly, I don’t think it’s even possible to capture everything in the code that is worth capturing. For example, why did you choose this implementation, as opposed to another that might be more obvious or common?

+

After our argument, I came up with a good rule of thumb (or “pattern”):

+

Write comments for your (future) self.1

+

In other words, if your comment will help you to understand the code more quickly when you look at it in the future, then it’s a valid comment. It also means that you can assume that the reader has about as much general programming knowledge as you currently do. (Your future self will have more general knowledge, but possibly less specific knowledge of the lines of code in question. And because of this, your current solution might not make as much sense in the future. You might know of a better solution in the future, but you’ll have to know all the constraints that you had when you originally wrote the code.)

+

This is not to say that you should not write comments in clear English, that others can understand. The comment is written for a future maintainer. That may be you (which is why the rule works well), or it may be someone else. The rule is more about when to write a comment, and what level of competence you should assume of the reader.

+

1 Perhaps it should be “Write comments TO your (future) self”.

+
+ + +
+ +
+
+ +

Bulk Rename in Bash

+ + + +
+

Here’s a relatively simple way to rename a bunch of files from the command line. It uses sed within a command substitution to compute the new names from the old names.

+

In this example, we’re renaming files that start with “ABC” to start with “XYZ” instead:

+
  for i in ABC*; do mv $i $(echo $i | sed -e s/^ABC/XYZ/); done
+

You’ll have to use shell globbing (wildcards) in the first part, to determine which files will be the source of the renaming, and regular expressions in the second part to translate the old names into the new names.

+

It’s a good idea to use echo in place of mv before running this, to see what the results will be, so you don’t make a mistake.

+

Depending on the situation, you may need to adapt this for your particular needs. For example, if you have too many files to rename, you would need to use xargs. Or sed might not be up for the task of transforming the names that you want. Or you might need to use find to find files within a hierarchy. As with any UNIX idiom, there are hundreds of variations on the theme.

+
+ + +
+ +
+
+ +

Introduction

+ + + +
+

Welcome to the BoochTek blog. BoochTek, LLC is a small web development company, based in St. Louis, Missouri. We specialize in Ruby on Rails, jQuery (JavaScript), and PHP.

+

This bog will be a place where we can share some of our experiences in web development, Open Source, programming, and technology in general.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/page/5.html b/public/page/5.html new file mode 100644 index 0000000..ca09cd3 --- /dev/null +++ b/public/page/5.html @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + +BoochTek, LLC – Page 5 – Web Development, Ruby on Rails, Open Source + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + +
+
+ +

Introduction

+ + + +
+

Welcome to the BoochTek blog. BoochTek, LLC is a small web development company, based in St. Louis, Missouri. We specialize in Ruby on Rails, jQuery (JavaScript), and PHP.

+

This bog will be a place where we can share some of our experiences in web development, Open Source, programming, and technology in general.

+
+ + +
+ + +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..d4a3d3d --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,3 @@ +User-agent: * +Disallow: /wp-admin/ +Allow: /wp-admin/admin-ajax.php diff --git a/public/tag/estimation.html b/public/tag/estimation.html new file mode 100644 index 0000000..b639221 --- /dev/null +++ b/public/tag/estimation.html @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + +estimation – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Estimation Isn’t Agile

+ + + +
+

I don’t believe that estimation should be part of any Agile practice.

+

One of our managers recently mentioned that we hadn’t met the “contract” that we had “committed” to in our last iteration. This was complete nonsense, because A) we hadn’t made any such commitments, and B) we completed many more story points than the previous iterations (and without inflating story points).

+

estimates-as-deadlines

+

But her language made me come to several realizations. First and foremost, estimates are contracts. Sure, they’re not supposed to be treated as commitments, but they almost always are. And what does the Agile Manifesto say about this? It says that we should value customer collaboration over contract negotiation, and responding to change over following a plan. So it’s pretty clear that treating estimates as commitments is completely counter to the Agile values.

+

Why does this matter? What benefits do the Agile values bring us? I think the biggest benefit they bring is changing the way that we work, so that we can better deliver value to our customers. Without Agile, we’d just keep working the way we’ve always done things. And that didn’t seem to be working out so well. If we follow the Agile values and principles, at least we’ll have a fighting chance of improving our ability to deliver value.

+

Ask yourself — have you ever seen a software development project that was on time and on budget? Where the estimates were spot-on? Of course not. For one, we’re terrible at estimating. For another, our plans change — either from external factors, or from what we learn as we go.

+

Improved Estimation

+

To me, Agile is also about facing reality — and embracing it. It realizes that we’re terrible at estimating. It realizes that plans change. Most Agile methodologies have some tricks to counteract Hofstadter’s law. Generally, we use relative story points instead of hours, and then use an empirical factor to convert points to hours.

+

When this works, it is better than any other estimation I’ve ever seen. But it doesn’t work very often. People have trouble with relative estimation. How do you set the basis for what a point means without relating it to actual hours? Affinity estimation could work, but then you have to remember what the basis was. We’ve got a large distributed team, and when we tried this, we couldn’t all remember what the basis was.

+

Since we couldn’t get affinity estimation to work, we tried changing to perfect hours (only powers of 2). But then people thought of them as time. When we took longer than the estimate on an individual story, managers and team members thought we were taking longer than we should have. So our estimates ended up causing problems.

+

What Can We Do Instead?

+

Managers want estimates so that they can have predictability. They want to know when new features will be available. Is there a better way to get what we need?

+

I believe there’s a better way — prioritization. If you work on the most important thing first, then the most important thing will get done first. We should always be working on the next most important thing.

+

What if there’s more than 1 thing that’s most important? Then you’ve failed. You’ve failed at logic if you can’t understand that only 1 thing can be most important. You’ve failed at prioritizing the customers’ needs. You’ve failed at project management.

+

Arguments

+

1. Why can’t you just tell us how long it will really take?

+

Because we don’t know. Because we can’t know. This is the first time we’ve ever implemented the functionality you’ve asked for. If we’d done it before, we’d just use that existing code. As Glenn Vanderburg pointed out in his excellent talk on Software Engineering, we’re not building software, we’re architecting it.

+

2. But we have to tell our customers what to expect.

+

Why? Is the product so bad that you can’t keep customers around without leading them on with future enhancements? And why do customers need exact dates? A general roadmap telling them what the priorities for upcoming features should be sufficient.

+

3. But we have to have messaging about new features.

+

OK. Then send out that messaging once the feature has made it to Staging. Or even after it’s been rolled out to Production.

+

4. But we’ve promised these new features to the customers by this date.

+

Ah, so you’ve made promises to the customer that you don’t have control over. Have you ever heard of “under-promise and over-deliver”? That’s how you create happy customers. Yet you’ve done just the opposite, haven’t you? And then you want to blame someone else.

+

Risk

+

Estimates are risk. But the risk doesn’t come at the end, when the estimates are shown to be incorrect. The risk was in asking for the estimates in the first place, and placing trust in them. Don’t do it. Don’t promise things that you can’t be sure of.

+

Embrace this reality. Embrace this uncertainty. Always focus on what’s most important. That’s how you make customers happy.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/tag/no-estimates.html b/public/tag/no-estimates.html new file mode 100644 index 0000000..cd350ab --- /dev/null +++ b/public/tag/no-estimates.html @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + +no estimates – BoochTek, LLC + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ +
+
+ + + + + +
+
+ +

Estimation Isn’t Agile

+ + + +
+

I don’t believe that estimation should be part of any Agile practice.

+

One of our managers recently mentioned that we hadn’t met the “contract” that we had “committed” to in our last iteration. This was complete nonsense, because A) we hadn’t made any such commitments, and B) we completed many more story points than the previous iterations (and without inflating story points).

+

estimates-as-deadlines

+

But her language made me come to several realizations. First and foremost, estimates are contracts. Sure, they’re not supposed to be treated as commitments, but they almost always are. And what does the Agile Manifesto say about this? It says that we should value customer collaboration over contract negotiation, and responding to change over following a plan. So it’s pretty clear that treating estimates as commitments is completely counter to the Agile values.

+

Why does this matter? What benefits do the Agile values bring us? I think the biggest benefit they bring is changing the way that we work, so that we can better deliver value to our customers. Without Agile, we’d just keep working the way we’ve always done things. And that didn’t seem to be working out so well. If we follow the Agile values and principles, at least we’ll have a fighting chance of improving our ability to deliver value.

+

Ask yourself — have you ever seen a software development project that was on time and on budget? Where the estimates were spot-on? Of course not. For one, we’re terrible at estimating. For another, our plans change — either from external factors, or from what we learn as we go.

+

Improved Estimation

+

To me, Agile is also about facing reality — and embracing it. It realizes that we’re terrible at estimating. It realizes that plans change. Most Agile methodologies have some tricks to counteract Hofstadter’s law. Generally, we use relative story points instead of hours, and then use an empirical factor to convert points to hours.

+

When this works, it is better than any other estimation I’ve ever seen. But it doesn’t work very often. People have trouble with relative estimation. How do you set the basis for what a point means without relating it to actual hours? Affinity estimation could work, but then you have to remember what the basis was. We’ve got a large distributed team, and when we tried this, we couldn’t all remember what the basis was.

+

Since we couldn’t get affinity estimation to work, we tried changing to perfect hours (only powers of 2). But then people thought of them as time. When we took longer than the estimate on an individual story, managers and team members thought we were taking longer than we should have. So our estimates ended up causing problems.

+

What Can We Do Instead?

+

Managers want estimates so that they can have predictability. They want to know when new features will be available. Is there a better way to get what we need?

+

I believe there’s a better way — prioritization. If you work on the most important thing first, then the most important thing will get done first. We should always be working on the next most important thing.

+

What if there’s more than 1 thing that’s most important? Then you’ve failed. You’ve failed at logic if you can’t understand that only 1 thing can be most important. You’ve failed at prioritizing the customers’ needs. You’ve failed at project management.

+

Arguments

+

1. Why can’t you just tell us how long it will really take?

+

Because we don’t know. Because we can’t know. This is the first time we’ve ever implemented the functionality you’ve asked for. If we’d done it before, we’d just use that existing code. As Glenn Vanderburg pointed out in his excellent talk on Software Engineering, we’re not building software, we’re architecting it.

+

2. But we have to tell our customers what to expect.

+

Why? Is the product so bad that you can’t keep customers around without leading them on with future enhancements? And why do customers need exact dates? A general roadmap telling them what the priorities for upcoming features should be sufficient.

+

3. But we have to have messaging about new features.

+

OK. Then send out that messaging once the feature has made it to Staging. Or even after it’s been rolled out to Production.

+

4. But we’ve promised these new features to the customers by this date.

+

Ah, so you’ve made promises to the customer that you don’t have control over. Have you ever heard of “under-promise and over-deliver”? That’s how you create happy customers. Yet you’ve done just the opposite, haven’t you? And then you want to blame someone else.

+

Risk

+

Estimates are risk. But the risk doesn’t come at the end, when the estimates are shown to be incorrect. The risk was in asking for the estimates in the first place, and placing trust in them. Don’t do it. Don’t promise things that you can’t be sure of.

+

Embrace this reality. Embrace this uncertainty. Always focus on what’s most important. That’s how you make customers happy.

+
+ + +
+ +
+
+ + + + +
+ + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/public/wp-content/plugins/akismet/_inc/form.js b/public/wp-content/plugins/akismet/_inc/form.js new file mode 100644 index 0000000..29667a3 --- /dev/null +++ b/public/wp-content/plugins/akismet/_inc/form.js @@ -0,0 +1,64 @@ +var _____WB$wombat$assign$function_____ = function(name) {return (self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init(name)) || self[name]; }; +if (!self.__WB_pmw) { self.__WB_pmw = function(obj) { this.__WB_source = obj; return this; } } +{ + let window = _____WB$wombat$assign$function_____("window"); + let self = _____WB$wombat$assign$function_____("self"); + let document = _____WB$wombat$assign$function_____("document"); + let location = _____WB$wombat$assign$function_____("location"); + let top = _____WB$wombat$assign$function_____("top"); + let parent = _____WB$wombat$assign$function_____("parent"); + let frames = _____WB$wombat$assign$function_____("frames"); + let opener = _____WB$wombat$assign$function_____("opener"); + +var ak_js = document.getElementById( "ak_js" ); + +if ( ! ak_js ) { + ak_js = document.createElement( 'input' ); + ak_js.setAttribute( 'id', 'ak_js' ); + ak_js.setAttribute( 'name', 'ak_js' ); + ak_js.setAttribute( 'type', 'hidden' ); +} +else { + ak_js.parentNode.removeChild( ak_js ); +} + +ak_js.setAttribute( 'value', ( new Date() ).getTime() ); + +var commentForm = document.getElementById( 'commentform' ); + +if ( commentForm ) { + commentForm.appendChild( ak_js ); +} +else { + var replyRowContainer = document.getElementById( 'replyrow' ); + + if ( replyRowContainer ) { + var children = replyRowContainer.getElementsByTagName( 'td' ); + + if ( children.length > 0 ) { + children[0].appendChild( ak_js ); + } + } +} + +} +/* + FILE ARCHIVED ON 00:22:30 Dec 06, 2022 AND RETRIEVED FROM THE + INTERNET ARCHIVE ON 08:35:51 Feb 23, 2024. + JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. + + ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. + SECTION 108(a)(3)). +*/ +/* +playback timings (ms): + exclusion.robots: 0.113 + exclusion.robots.policy: 0.099 + cdx.remote: 0.1 + esindex: 0.013 + LoadShardBlock: 123.176 (6) + PetaboxLoader3.datanode: 111.781 (8) + load_resource: 172.026 + PetaboxLoader3.resolve: 103.083 + loaddict: 55.232 +*/ \ No newline at end of file diff --git a/public/wp-content/plugins/akismet/_inc/form.js?ver=3.0.4 b/public/wp-content/plugins/akismet/_inc/form.js?ver=3.0.4 new file mode 100644 index 0000000..e997164 --- /dev/null +++ b/public/wp-content/plugins/akismet/_inc/form.js?ver=3.0.4 @@ -0,0 +1,63 @@ +var _____WB$wombat$assign$function_____ = function(name) {return (self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init(name)) || self[name]; }; +if (!self.__WB_pmw) { self.__WB_pmw = function(obj) { this.__WB_source = obj; return this; } } +{ + let window = _____WB$wombat$assign$function_____("window"); + let self = _____WB$wombat$assign$function_____("self"); + let document = _____WB$wombat$assign$function_____("document"); + let location = _____WB$wombat$assign$function_____("location"); + let top = _____WB$wombat$assign$function_____("top"); + let parent = _____WB$wombat$assign$function_____("parent"); + let frames = _____WB$wombat$assign$function_____("frames"); + let opener = _____WB$wombat$assign$function_____("opener"); + +var ak_js = document.getElementById( "ak_js" ); + +if ( ! ak_js ) { + ak_js = document.createElement( 'input' ); + ak_js.setAttribute( 'id', 'ak_js' ); + ak_js.setAttribute( 'name', 'ak_js' ); + ak_js.setAttribute( 'type', 'hidden' ); +} +else { + ak_js.parentNode.removeChild( ak_js ); +} + +ak_js.setAttribute( 'value', ( new Date() ).getTime() ); + +var commentForm = document.getElementById( 'commentform' ); + +if ( commentForm ) { + commentForm.appendChild( ak_js ); +} +else { + var replyRowContainer = document.getElementById( 'replyrow' ); + + if ( replyRowContainer ) { + var children = replyRowContainer.getElementsByTagName( 'td' ); + + if ( children.length > 0 ) { + children[0].appendChild( ak_js ); + } + } +} + +} +/* + FILE ARCHIVED ON 17:09:07 Dec 17, 2015 AND RETRIEVED FROM THE + INTERNET ARCHIVE ON 08:36:01 Feb 23, 2024. + JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. + + ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. + SECTION 108(a)(3)). +*/ +/* +playback timings (ms): + exclusion.robots: 0.211 + exclusion.robots.policy: 0.194 + cdx.remote: 0.147 + esindex: 0.016 + LoadShardBlock: 907.746 (6) + PetaboxLoader3.resolve: 173.783 (3) + PetaboxLoader3.datanode: 194.953 (7) + load_resource: 167.046 +*/ \ No newline at end of file diff --git a/public/wp-content/plugins/akismet/_inc/form.js?ver=3.1.6 b/public/wp-content/plugins/akismet/_inc/form.js?ver=3.1.6 new file mode 100644 index 0000000..f9628f9 --- /dev/null +++ b/public/wp-content/plugins/akismet/_inc/form.js?ver=3.1.6 @@ -0,0 +1,63 @@ +var _____WB$wombat$assign$function_____ = function(name) {return (self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init(name)) || self[name]; }; +if (!self.__WB_pmw) { self.__WB_pmw = function(obj) { this.__WB_source = obj; return this; } } +{ + let window = _____WB$wombat$assign$function_____("window"); + let self = _____WB$wombat$assign$function_____("self"); + let document = _____WB$wombat$assign$function_____("document"); + let location = _____WB$wombat$assign$function_____("location"); + let top = _____WB$wombat$assign$function_____("top"); + let parent = _____WB$wombat$assign$function_____("parent"); + let frames = _____WB$wombat$assign$function_____("frames"); + let opener = _____WB$wombat$assign$function_____("opener"); + +var ak_js = document.getElementById( "ak_js" ); + +if ( ! ak_js ) { + ak_js = document.createElement( 'input' ); + ak_js.setAttribute( 'id', 'ak_js' ); + ak_js.setAttribute( 'name', 'ak_js' ); + ak_js.setAttribute( 'type', 'hidden' ); +} +else { + ak_js.parentNode.removeChild( ak_js ); +} + +ak_js.setAttribute( 'value', ( new Date() ).getTime() ); + +var commentForm = document.getElementById( 'commentform' ); + +if ( commentForm ) { + commentForm.appendChild( ak_js ); +} +else { + var replyRowContainer = document.getElementById( 'replyrow' ); + + if ( replyRowContainer ) { + var children = replyRowContainer.getElementsByTagName( 'td' ); + + if ( children.length > 0 ) { + children[0].appendChild( ak_js ); + } + } +} + +} +/* + FILE ARCHIVED ON 17:42:59 Nov 07, 2016 AND RETRIEVED FROM THE + INTERNET ARCHIVE ON 08:36:11 Feb 23, 2024. + JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. + + ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. + SECTION 108(a)(3)). +*/ +/* +playback timings (ms): + exclusion.robots: 0.091 + exclusion.robots.policy: 0.082 + cdx.remote: 0.089 + esindex: 0.01 + LoadShardBlock: 94.458 (6) + PetaboxLoader3.datanode: 118.777 (7) + load_resource: 142.75 + PetaboxLoader3.resolve: 93.593 +*/ \ No newline at end of file diff --git a/public/wp-content/plugins/akismet/_inc/form.js?ver=4.0.3 b/public/wp-content/plugins/akismet/_inc/form.js?ver=4.0.3 new file mode 100644 index 0000000..1a41b11 --- /dev/null +++ b/public/wp-content/plugins/akismet/_inc/form.js?ver=4.0.3 @@ -0,0 +1,64 @@ +var _____WB$wombat$assign$function_____ = function(name) {return (self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init(name)) || self[name]; }; +if (!self.__WB_pmw) { self.__WB_pmw = function(obj) { this.__WB_source = obj; return this; } } +{ + let window = _____WB$wombat$assign$function_____("window"); + let self = _____WB$wombat$assign$function_____("self"); + let document = _____WB$wombat$assign$function_____("document"); + let location = _____WB$wombat$assign$function_____("location"); + let top = _____WB$wombat$assign$function_____("top"); + let parent = _____WB$wombat$assign$function_____("parent"); + let frames = _____WB$wombat$assign$function_____("frames"); + let opener = _____WB$wombat$assign$function_____("opener"); + +var ak_js = document.getElementById( "ak_js" ); + +if ( ! ak_js ) { + ak_js = document.createElement( 'input' ); + ak_js.setAttribute( 'id', 'ak_js' ); + ak_js.setAttribute( 'name', 'ak_js' ); + ak_js.setAttribute( 'type', 'hidden' ); +} +else { + ak_js.parentNode.removeChild( ak_js ); +} + +ak_js.setAttribute( 'value', ( new Date() ).getTime() ); + +var commentForm = document.getElementById( 'commentform' ); + +if ( commentForm ) { + commentForm.appendChild( ak_js ); +} +else { + var replyRowContainer = document.getElementById( 'replyrow' ); + + if ( replyRowContainer ) { + var children = replyRowContainer.getElementsByTagName( 'td' ); + + if ( children.length > 0 ) { + children[0].appendChild( ak_js ); + } + } +} + +} +/* + FILE ARCHIVED ON 14:53:15 Dec 05, 2022 AND RETRIEVED FROM THE + INTERNET ARCHIVE ON 08:36:20 Feb 23, 2024. + JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. + + ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. + SECTION 108(a)(3)). +*/ +/* +playback timings (ms): + exclusion.robots: 0.12 + exclusion.robots.policy: 0.109 + cdx.remote: 0.096 + esindex: 0.008 + LoadShardBlock: 262.51 (6) + PetaboxLoader3.datanode: 173.322 (8) + load_resource: 337.991 + PetaboxLoader3.resolve: 195.089 + loaddict: 114.478 +*/ \ No newline at end of file diff --git a/public/wp-content/themes/twentyeleven/images/comment-arrow.png b/public/wp-content/themes/twentyeleven/images/comment-arrow.png new file mode 100644 index 0000000..60a6d5d Binary files /dev/null and b/public/wp-content/themes/twentyeleven/images/comment-arrow.png differ diff --git a/public/wp-content/themes/twentyeleven/images/headers/wheel.jpg b/public/wp-content/themes/twentyeleven/images/headers/wheel.jpg new file mode 100644 index 0000000..c5b9878 Binary files /dev/null and b/public/wp-content/themes/twentyeleven/images/headers/wheel.jpg differ diff --git a/public/wp-content/themes/twentyeleven/images/search.png b/public/wp-content/themes/twentyeleven/images/search.png new file mode 100644 index 0000000..5036704 Binary files /dev/null and b/public/wp-content/themes/twentyeleven/images/search.png differ diff --git a/public/wp-content/themes/twentyeleven/style.css b/public/wp-content/themes/twentyeleven/style.css new file mode 100644 index 0000000..716d635 --- /dev/null +++ b/public/wp-content/themes/twentyeleven/style.css @@ -0,0 +1,2794 @@ +/* +Theme Name: Twenty Eleven +Theme URI: https://wordpress.org/themes/twentyeleven/ +Author: the WordPress team +Author URI: https://wordpress.org/ +Description: The 2011 theme for WordPress is sophisticated, lightweight, and adaptable. Make it yours with a custom menu, header image, and background -- then go further with available theme options for light or dark color scheme, custom link colors, and three layout choices. Twenty Eleven comes equipped with a Showcase page template that transforms your front page into a showcase to show off your best content, widget support galore (sidebar, three footer areas, and a Showcase page widget area), and a custom "Ephemera" widget to display your Aside, Link, Quote, or Status posts. Included are styles for print and for the admin editor, support for featured images (as custom header images on posts and pages and as large images on featured "sticky" posts), and special styles for six different post formats. +Version: 2.3 +License: GNU General Public License v2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html +Tags: dark, light, white, black, gray, one-column, two-columns, left-sidebar, right-sidebar, fixed-layout, responsive-layout, custom-background, custom-colors, custom-header, custom-menu, editor-style, featured-image-header, featured-images, flexible-header, full-width-template, microformats, post-formats, rtl-language-support, sticky-post, theme-options, translation-ready +Text Domain: twentyeleven +*/ + +/* =Reset default browser CSS. Based on work by Eric Meyer. +-------------------------------------------------------------- */ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + border: 0; + font-family: inherit; + font-size: 100%; + font-style: inherit; + font-weight: inherit; + margin: 0; + outline: 0; + padding: 0; + vertical-align: baseline; +} +:focus {/* remember to define focus styles! */ + outline: 0; +} +body { + background: #fff; + line-height: 1; +} +ol, ul { + list-style: none; +} +table {/* tables still need 'cellspacing="0"' in the markup */ + border-collapse: separate; + border-spacing: 0; +} +caption, th, td { + font-weight: normal; + text-align: left; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ""; +} +blockquote, q { + quotes: "" ""; +} +a img { + border: 0; +} +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + + +/* =Structure +----------------------------------------------- */ + +body { + padding: 0 2em; +} +#page { + margin: 2em auto; + max-width: 1000px; +} +#branding hgroup { + margin: 0 7.6%; +} +#access div { + margin: 0 7.6%; +} +#primary { + float: left; + margin: 0 -26.4% 0 0; + width: 100%; +} +#content { + margin: 0 34% 0 7.6%; + width: 58.4%; +} +#secondary { + float: right; + margin-right: 7.6%; + width: 18.8%; +} + +/* Singular */ +.singular #primary { + margin: 0; +} +.singular #content, +.left-sidebar.singular #content { + margin: 0 7.6%; + position: relative; + width: auto; +} +.singular .entry-header, +.singular .entry-content, +.singular footer.entry-meta, +.singular #comments-title { + margin: 0 auto; + width: 68.9%; +} + +/* Attachments */ +.singular .image-attachment .entry-content { + margin: 0 auto; + width: auto; +} +.singular .image-attachment .entry-description { + margin: 0 auto; + width: 68.9%; +} + +/* Showcase */ +.page-template-showcase-php #primary, +.left-sidebar.page-template-showcase-php #primary { + margin: 0; +} +.page-template-showcase-php #content, +.left-sidebar.page-template-showcase-php #content { + margin: 0 7.6%; + width: auto; +} +.page-template-showcase-php section.recent-posts { + float: right; + margin: 0 0 0 31%; + width: 69%; +} +.page-template-showcase-php #main .widget-area { + float: left; + margin: 0 -22.15% 0 0; + width: 22.15%; +} + +/* error404 */ +.error404 #primary { + float: none; + margin: 0; +} +.error404 #primary #content { + margin: 0 7.6%; + width: auto; +} + +/* Alignment */ +.alignleft { + display: inline; + float: left; + margin-right: 1.625em; +} +.alignright { + display: inline; + float: right; + margin-left: 1.625em; +} +.aligncenter { + clear: both; + display: block; + margin-left: auto; + margin-right: auto; +} + +/* Right Content */ +.left-sidebar #primary { + float: right; + margin: 0 0 0 -26.4%; + width: 100%; +} +.left-sidebar #content { + margin: 0 7.6% 0 34%; + width: 58.4%; +} +.left-sidebar #secondary { + float: left; + margin-left: 7.6%; + margin-right: 0; + width: 18.8%; +} + +/* One column */ +.one-column #page { + max-width: 690px; +} +.one-column #content { + margin: 0 7.6%; + width: auto; +} +.one-column #nav-below { + border-bottom: 1px solid #ddd; + margin-bottom: 1.625em; +} +.one-column #secondary { + float: none; + margin: 0 7.6%; + width: auto; +} +/* Simplify the showcase template */ +.one-column .page-template-showcase-php section.recent-posts { + float: none; + margin: 0; + width: 100%; +} +.one-column .page-template-showcase-php #main .widget-area { + float: none; + margin: 0; + width: auto; +} +.one-column .page-template-showcase-php .other-recent-posts { + border-bottom: 1px solid #ddd; +} +/* Simplify the showcase template when small feature */ +.one-column section.featured-post .attachment-small-feature { + border: none; + display: block; + height: auto; + max-width: 60%; + position: static; +} +.one-column article.feature-image.small { + margin: 0 0 1.625em; + padding: 0; +} +.one-column article.feature-image.small .entry-title { + font-size: 20px; + line-height: 1.3em; +} +.one-column article.feature-image.small .entry-summary { + height: 150px; + overflow: hidden; + padding: 0; + text-overflow: ellipsis; +} +.one-column article.feature-image.small .entry-summary a { + left: -9%; +} +/* Remove the margin on singular articles */ +.one-column.singular .entry-header, +.one-column.singular .entry-content, +.one-column.singular footer.entry-meta, +.one-column.singular #comments-title { + width: 100%; +} +/* Simplify the pullquotes and pull styles */ +.one-column.singular blockquote.pull { + margin: 0 0 1.625em; +} +.one-column.singular .pull.alignleft { + margin: 0 1.625em 0 0; +} +.one-column.singular .pull.alignright { + margin: 0 0 0 1.625em; +} +.one-column.singular .entry-meta .edit-link a { + position: absolute; + left: 0; + top: 40px; +} +.one-column.singular #author-info { + margin: 2.2em -8.8% 0; + padding: 20px 8.8%; +} +/* Make sure we have room for our comment avatars */ +.one-column .commentlist > li.comment { + margin-left: 102px; + width: auto; +} +/* Make sure the logo and search form don't collide */ +.one-column #branding #searchform { + right: 40px; + top: 4em; +} +/* Talking avatars take up too much room at this size */ +.one-column .commentlist > li.comment { + margin-left: 0; +} +.one-column .commentlist > li.comment .comment-meta, +.one-column .commentlist > li.comment .comment-content { + margin-right: 85px; +} +.one-column .commentlist .avatar { + background: transparent; + display: block; + padding: 0; + top: 1.625em; + left: auto; + right: 1.625em; +} +.one-column .commentlist .children .avatar { + background: none; + padding: 0; + position: absolute; + top: 2.2em; + left: 2.2em; +} +.one-column #respond { + width: auto; +} + + +/* =Global +----------------------------------------------- */ + +body, input, textarea { + color: #373737; + font: 15px "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; + line-height: 1.625; +} +body { + background: #e2e2e2; +} +#page { + background: #fff; +} + +/* Headings */ +h1,h2,h3,h4,h5,h6 { + clear: both; +} +hr { + background-color: #ccc; + border: 0; + height: 1px; + margin-bottom: 1.625em; +} + +/* Text elements */ +p { + margin-bottom: 1.625em; +} +ul, ol { + margin: 0 0 1.625em 2.5em; +} +ul { + list-style: square; +} +ol { + list-style-type: decimal; +} +ol ol { + list-style: upper-alpha; +} +ol ol ol { + list-style: lower-roman; +} +ol ol ol ol { + list-style: lower-alpha; +} +ul ul, ol ol, ul ol, ol ul { + margin-bottom: 0; +} +dl { + margin: 0 1.625em; +} +dt { + font-weight: bold; +} +dd { + margin-bottom: 1.625em; +} +strong { + font-weight: bold; +} +cite, em, i { + font-style: italic; +} +blockquote { + font-family: Georgia, "Bitstream Charter", serif; + font-style: italic; + font-weight: normal; + margin: 0 3em; +} +blockquote em, blockquote i, blockquote cite { + font-style: normal; +} +blockquote cite { + color: #666; + font: 12px "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; + letter-spacing: 0.05em; + text-transform: uppercase; +} +pre { + background: #f4f4f4; + font: 13px "Courier 10 Pitch", Courier, monospace; + line-height: 1.5; + margin-bottom: 1.625em; + overflow: auto; + padding: 0.75em 1.625em; +} +code, kbd, samp, var { + font: 13px Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; +} +abbr, acronym, dfn { + border-bottom: 1px dotted #666; + cursor: help; +} +address { + display: block; + margin: 0 0 1.625em; +} +ins { + background: #fff9c0; + text-decoration: none; +} +sup, +sub { + font-size: 10px; + height: 0; + line-height: 1; + position: relative; + vertical-align: baseline; +} +sup { + bottom: 1ex; +} +sub { + top: .5ex; +} +small { + font-size: smaller; +} + +/* Forms */ +input[type=text], +input[type=password], +input[type=email], +input[type=url], +input[type=number], +textarea { + background: #fafafa; + -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.1); + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.1); + box-shadow: inset 0 1px 1px rgba(0,0,0,0.1); + border: 1px solid #ddd; + color: #888; +} +input[type=text]:focus, +input[type=password]:focus, +input[type=email]:focus, +input[type=url]:focus, +input[type=number]:focus, +textarea:focus { + color: #373737; +} +textarea { + padding-left: 3px; + width: 98%; +} +input[type=text], +input[type=password], +input[type=email], +input[type=url], +input[type=number] { + padding: 3px; +} +input#s { + background: url(/web/20151217170907im_/http://blog.boochtek.com/wp-content/themes/twentyeleven/images/search.png) no-repeat 5px 6px; + -moz-border-radius: 2px; + border-radius: 2px; + font-size: 14px; + height: 22px; + line-height: 1.2em; + padding: 4px 10px 4px 28px; +} +input#searchsubmit { + display: none; +} + +/* Links */ +a { + color: #1982d1; + text-decoration: none; +} +a:focus, +a:active, +a:hover { + text-decoration: underline; +} + +/* Assistive text */ +.assistive-text, +.screen-reader-text { + position: absolute !important; + clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ + clip: rect(1px, 1px, 1px, 1px); + overflow: hidden; + height: 1px; + width: 1px; +} +#access a.assistive-text:focus, +.screen-reader-text:hover, +.screen-reader-text:active, +.screen-reader-text:focus { + background: #eee; + border-bottom: 1px solid #ddd; + color: #1982d1; + clip: auto !important; + font-size: 12px; + height: auto; + position: absolute; + text-decoration: underline; + top: 0; + left: 7.6%; + width: auto; +} + + +/* =Header +----------------------------------------------- */ + +#branding { + border-top: 2px solid #bbb; + padding-bottom: 10px; + position: relative; + z-index: 9999; +} +#site-title { + margin-right: 270px; + padding: 3.65625em 0 0; +} +#site-title a { + color: #111; + font-size: 30px; + font-weight: bold; + line-height: 36px; + text-decoration: none; +} +#site-title a:hover, +#site-title a:focus, +#site-title a:active { + color: #1982d1; +} +#site-description { + color: #7a7a7a; + font-size: 14px; + margin: 0 270px 3.65625em 0; +} +#branding img { + height: auto; + display: block; + width: 100%; +} + + +/* =Menu +-------------------------------------------------------------- */ + +#access { + background: #222; /* Show a solid color for older browsers */ + background: -moz-linear-gradient(#252525, #0a0a0a); + background: -o-linear-gradient(#252525, #0a0a0a); + background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#252525), to(#0a0a0a)); /* older webkit syntax */ + background: -webkit-linear-gradient(#252525, #0a0a0a); + -webkit-box-shadow: rgba(0, 0, 0, 0.4) 0px 1px 2px; + -moz-box-shadow: rgba(0, 0, 0, 0.4) 0px 1px 2px; + box-shadow: rgba(0, 0, 0, 0.4) 0px 1px 2px; + clear: both; + display: block; + float: left; + margin: 0 auto 6px; + width: 100%; +} +#access ul { + font-size: 13px; + list-style: none; + margin: 0 0 0 -0.8125em; + padding-left: 0; +} +#access li { + float: left; + position: relative; +} +#access a { + color: #eee; + display: block; + line-height: 3.333em; + padding: 0 1.2125em; + text-decoration: none; +} +#access ul ul { + -moz-box-shadow: 0 3px 3px rgba(0,0,0,0.2); + -webkit-box-shadow: 0 3px 3px rgba(0,0,0,0.2); + box-shadow: 0 3px 3px rgba(0,0,0,0.2); + display: none; + float: left; + margin: 0; + position: absolute; + top: 3.333em; + left: 0; + width: 188px; + z-index: 99999; +} +#access ul ul ul { + left: 100%; + top: 0; +} +#access ul ul a { + background: #f9f9f9; + border-bottom: 1px dotted #ddd; + color: #444; + font-size: 13px; + font-weight: normal; + height: auto; + line-height: 1.4em; + padding: 10px 10px; + width: 168px; +} +#access li:hover > a, +#access ul ul :hover > a, +#access a:focus { + background: #efefef; +} +#access li:hover > a, +#access a:focus { + background: #f9f9f9; /* Show a solid color for older browsers */ + background: -moz-linear-gradient(#f9f9f9, #e5e5e5); + background: -o-linear-gradient(#f9f9f9, #e5e5e5); + background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#f9f9f9), to(#e5e5e5)); /* Older webkit syntax */ + background: -webkit-linear-gradient(#f9f9f9, #e5e5e5); + color: #373737; +} +#access ul li:hover > ul { + display: block; +} +#access .current-menu-item > a, +#access .current-menu-ancestor > a, +#access .current_page_item > a, +#access .current_page_ancestor > a { + font-weight: bold; +} + +/* Search Form */ +#branding #searchform { + position: absolute; + top: 3.8em; + right: 7.6%; + text-align: right; +} +#branding #searchform div { + margin: 0; +} +#branding #s { + float: right; + -webkit-transition-duration: 400ms; + -webkit-transition-property: width, background; + -webkit-transition-timing-function: ease; + -moz-transition-duration: 400ms; + -moz-transition-property: width, background; + -moz-transition-timing-function: ease; + -o-transition-duration: 400ms; + -o-transition-property: width, background; + -o-transition-timing-function: ease; + width: 72px; +} +#branding #s:focus { + background-color: #f9f9f9; + width: 196px; +} +#branding #searchsubmit { + display: none; +} +#branding .only-search #searchform { + top: 5px; + z-index: 1; +} +#branding .only-search #s { + background-color: #666; + border-color: #000; + color: #222; +} +#branding .only-search #s, +#branding .only-search #s:focus { + width: 85%; +} +#branding .only-search #s:focus { + background-color: #bbb; +} +#branding .with-image #searchform { + top: auto; + bottom: -27px; + max-width: 195px; +} +#branding .only-search + #access div { + padding-right: 205px; +} + + +/* =Content +----------------------------------------------- */ + +#main { + clear: both; + padding: 1.625em 0 0; +} +.page-title { + color: #666; + font-size: 10px; + font-weight: 500; + letter-spacing: 0.1em; + line-height: 2.6em; + margin: 0 0 2.6em; + text-transform: uppercase; +} +.page-title a { + font-size: 12px; + font-weight: bold; + letter-spacing: 0; + text-transform: none; +} +.hentry, +.no-results { + border-bottom: 1px solid #ddd; + margin: 0 0 1.625em; + padding: 0 0 1.625em; + position: relative; +} +.hentry:last-child, +.no-results { + border-bottom: none; +} +.blog .sticky .entry-header .entry-meta { + clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ + clip: rect(1px, 1px, 1px, 1px); + position: absolute !important; +} +.entry-title, +.entry-header .entry-meta { + padding-right: 76px; +} +.entry-title { + clear: both; + color: #222; + font-size: 26px; + font-weight: bold; + line-height: 1.5em; + padding-bottom: .3em; + padding-top: 15px; +} +.entry-title, +.entry-title a { + color: #222; + text-decoration: none; +} +.entry-title a:hover, +.entry-title a:focus, +.entry-title a:active { + color: #1982d1; +} +.entry-meta { + color: #666; + clear: both; + font-size: 12px; + line-height: 18px; +} +.entry-meta a { + font-weight: bold; +} +.single-author .entry-meta .by-author { + display: none; +} +.entry-content, +.entry-summary { + padding: 1.625em 0 0; +} +.entry-content .more-link { + white-space: nowrap; +} +.entry-content h1, +.entry-content h2, +.comment-content h1, +.comment-content h2 { + color: #000; + font-weight: bold; + margin: 0 0 .8125em; +} +.entry-content h3, +.comment-content h3 { + font-size: 10px; + letter-spacing: 0.1em; + line-height: 2.6em; + text-transform: uppercase; +} +.entry-content table, +.comment-content table { + border-bottom: 1px solid #ddd; + margin: 0 0 1.625em; + width: 100%; +} +.entry-content th, +.comment-content th { + color: #666; + font-size: 10px; + font-weight: 500; + letter-spacing: 0.1em; + line-height: 2.6em; + text-transform: uppercase; +} +.entry-content td, +.comment-content td { + border-top: 1px solid #ddd; + padding: 6px 10px 6px 0; +} +.entry-content #s { + width: 75%; +} +.comment-content ul, +.comment-content ol { + margin-bottom: 1.625em; +} +.comment-content ul ul, +.comment-content ol ol, +.comment-content ul ol, +.comment-content ol ul { + margin-bottom: 0; +} +dl.gallery-item { + margin: 0; +} +.page-link { + clear: both; + display: block; + margin: 0 0 1.625em; +} +.page-link a { + background: #eee; + color: #373737; + margin: 0; + padding: 2px 3px; + text-decoration: none; +} +.page-link a:hover { + background: #888; + color: #fff; + font-weight: bold; +} +.page-link span { + margin-right: 6px; +} +.entry-meta .edit-link a, +.commentlist .edit-link a { + background: #eee; + -moz-border-radius: 3px; + border-radius: 3px; + color: #666; + float: right; + font-size: 12px; + line-height: 1.5em; + font-weight: 300; + text-decoration: none; + padding: 0 8px; +} +.entry-meta .edit-link a:hover, +.commentlist .edit-link a:hover { + background: #888; + color: #fff; +} +.entry-content .edit-link { + clear: both; + display: block; +} + +/* Images */ +.entry-content img, +.comment-content img, +.widget img { + max-width: 100%; /* Fluid images for posts, comments, and widgets */ +} +img[class*="align"], +img[class*="wp-image-"], +img[class*="attachment-"] { + height: auto; /* Make sure images with WordPress-added height and width attributes are scaled correctly */ +} +img.size-full, +img.size-large { + max-width: 97.5%; + width: auto; /* Prevent stretching of full-size and large-size images with height and width attributes in IE8 */ + height: auto; /* Make sure images with WordPress-added height and width attributes are scaled correctly */ +} +.entry-content img.wp-smiley { + border: none; + margin-bottom: 0; + margin-top: 0; + padding: 0; +} +img.alignleft, +img.alignright, +img.aligncenter { + margin-bottom: 1.625em; +} +p img, +.wp-caption { + margin-top: 0.4em; +} +.wp-caption { + background: #eee; + margin-bottom: 1.625em; + max-width: 96%; + padding: 9px; +} +.wp-caption img { + display: block; + margin: -2px 0 0 -2px; + max-width: 98%; +} +.wp-caption .wp-caption-text, +.gallery-caption { + color: #666; + font-family: Georgia, serif; + font-size: 12px; +} +.wp-caption .wp-caption-text { + margin-bottom: 0.6em; + padding: 10px 0 5px 40px; + position: relative; +} +.wp-caption .wp-caption-text:before { + color: #666; + content: '\2014'; + font-size: 14px; + font-style: normal; + font-weight: bold; + margin-right: 5px; + position: absolute; + left: 10px; + top: 7px; +} +#content .gallery { + margin: 0 auto 1.625em; +} +#content .gallery a img { + border: none; +} +img#wpstats { + display: block; + margin: 0 auto 1.625em; +} +#content .gallery-columns-4 .gallery-item { + width: 23%; + padding-right: 2%; +} +#content .gallery-columns-4 .gallery-item img { + width: 100%; + height: auto; +} + +/* Image borders */ +img[class*="align"], +img[class*="wp-image-"], +#content .gallery .gallery-icon img {/* Add fancy borders to all WordPress-added images but not things like badges and icons and the like */ + border: 1px solid #ddd; + padding: 6px; + max-width: 97.5%; +} +.wp-caption img { + border-color: #eee; +} +a:focus img[class*="align"], +a:hover img[class*="align"], +a:active img[class*="align"], +a:focus img[class*="wp-image-"], +a:hover img[class*="wp-image-"], +a:active img[class*="wp-image-"], +#content .gallery .gallery-icon a:focus img, +#content .gallery .gallery-icon a:hover img, +#content .gallery .gallery-icon a:active img {/* Add some useful style to those fancy borders for linked images ... */ + background: #eee; + border-color: #bbb; +} +.wp-caption a:focus img, +.wp-caption a:active img, +.wp-caption a:hover img {/* ... including captioned images! */ + background: #fff; + border-color: #ddd; +} + +/* Make sure videos and embeds fit their containers */ +embed, +iframe, +object { + max-width: 100%; +} +.entry-content .twitter-tweet-rendered { + max-width: 100% !important; /* Override the Twitter embed fixed width */ +} + +/* Password Protected Posts */ +.post-password-required .entry-header .comments-link { + margin: 1.625em 0 0; +} +.post-password-required input[type=password] { + margin: 0.8125em 0; +} +.post-password-required input[type=password]:focus { + background: #f7f7f7; +} + +/* Author Info */ +#author-info { + font-size: 12px; + overflow: hidden; +} +.singular #author-info { + background: #f9f9f9; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 2.2em -35.6% 0 -35.4%; + padding: 20px 35.4%; +} +.archive #author-info { + border-bottom: 1px solid #ddd; + margin: 0 0 2.2em; + padding: 0 0 2.2em; +} +#author-avatar { + float: left; + margin-right: -78px; +} +#author-avatar img { + background: #fff; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 2px #bbb; + -moz-box-shadow: 0 1px 2px #bbb; + box-shadow: 0 1px 2px #bbb; + padding: 3px; +} +#author-description { + float: left; + margin-left: 108px; +} +#author-description h2 { + color: #000; + font-size: 15px; + font-weight: bold; + margin: 5px 0 10px; +} + +/* Comments link */ +.entry-header .comments-link a { + background: #eee url(/web/20151217170907im_/http://blog.boochtek.com/wp-content/themes/twentyeleven/images/comment-bubble.png) no-repeat; + color: #666; + font-size: 13px; + font-weight: normal; + line-height: 35px; + overflow: hidden; + padding: 0 0 0; + position: absolute; + top: 1.5em; + right: 0; + text-align: center; + text-decoration: none; + width: 43px; + height: 36px; +} +.entry-header .comments-link a:hover, +.entry-header .comments-link a:focus, +.entry-header .comments-link a:active { + background-color: #1982d1; + color: #fff; + color: rgba(255,255,255,0.8); +} +.entry-header .comments-link .leave-reply { + visibility: hidden; +} + +/* +Post Formats Headings +To hide the headings, display: none the ".entry-header .entry-format" selector, +and remove the padding rules below. +*/ +.entry-header .entry-format { + color: #666; + font-size: 10px; + font-weight: 500; + letter-spacing: 0.1em; + line-height: 2.6em; + position: absolute; + text-transform: uppercase; + top: -5px; +} +.entry-header hgroup .entry-title { + padding-top: 15px; +} +article.format-aside .entry-content, +article.format-link .entry-content, +article.format-status .entry-content { + padding: 20px 0 0; +} +article.format-status .entry-content { + min-height: 65px; +} +.recent-posts .entry-header .entry-format { + display: none; +} +.recent-posts .entry-header hgroup .entry-title { + padding-top: 0; +} + +/* Singular content styles for Posts and Pages */ +.singular .hentry { + border-bottom: none; + padding: 4.875em 0 0; + position: relative; +} +.singular.page .hentry { + padding: 3.5em 0 0; +} +.singular .entry-title { + color: #000; + font-size: 36px; + font-weight: bold; + line-height: 48px; +} +.singular .entry-title, +.singular .entry-header .entry-meta { + padding-right: 0; +} +.singular .entry-header .entry-meta { + position: absolute; + top: 0; + left: 0; +} +blockquote.pull { + font-size: 21px; + font-weight: bold; + line-height: 1.6125em; + margin: 0 0 1.625em; + text-align: center; +} +.singular blockquote.pull { + margin: 0 -22.25% 1.625em; +} +.pull.alignleft { + margin: 0 1.625em 0 0; + text-align: right; +} +.singular .pull.alignleft { + margin: 0 1.625em 0 -22.25%; +} +.pull.alignright { + margin: 0 0 0 1.625em; + text-align: left; +} +blockquote.pull.alignleft, +blockquote.pull.alignright { + width: 33%; +} +.singular .pull.alignright { + margin: 0 -22.25% 0 1.625em; +} +.singular blockquote.pull.alignleft, +.singular blockquote.pull.alignright { + width: 33%; +} +.singular .entry-meta .edit-link a { + bottom: auto; + left: 50px; + position: absolute; + right: auto; + top: 80px; +} + + +/* =Aside +----------------------------------------------- */ + +.format-aside .entry-title, +.format-aside .entry-header .comments-link { + display: none; +} +.singular .format-aside .entry-title { + display: block; +} +.format-aside .entry-content { + padding: 0; +} +.singular .format-aside .entry-content { + padding: 1.625em 0 0; +} + + +/* =Link +----------------------------------------------- */ + +.format-link .entry-title, +.format-link .entry-header .comments-link { + display: none; +} +.singular .format-link .entry-title { + display: block; +} +.format-link .entry-content { + padding: 0; +} +.singular .format-link .entry-content { + padding: 1.625em 0 0; +} + + +/* =Gallery +----------------------------------------------- */ + +.format-gallery .gallery-thumb { + float: left; + display: block; + margin: .375em 1.625em 0 0; + max-width: 100%; +} + + +/* =Status +----------------------------------------------- */ + +.format-status .entry-title, +.format-status .entry-header .comments-link { + display: none; +} +.singular .format-status .entry-title { + display: block; +} +.format-status .entry-content { + padding: 0; +} +.singular .format-status .entry-content { + padding: 1.625em 0 0; +} +.format-status img.avatar { + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 2px #ccc; + -moz-box-shadow: 0 1px 2px #ccc; + box-shadow: 0 1px 2px #ccc; + float: left; + margin: 4px 10px 2px 0; + padding: 0; +} + +/* =Standard +----------------------------------------------- */ + +.format-standard .wp-video, +.format-standard .wp-audio-shortcode, +.format-audio .wp-audio-shortcode, +.format-standard .video-player { + margin-bottom: 24px; +} + +/* =Quote +----------------------------------------------- */ + +.format-quote blockquote { + color: #555; + font-size: 17px; + margin: 0; +} + + +/* =Image +----------------------------------------------- */ + +.indexed.format-image .entry-header { + min-height: 61px; /* Prevent the comment icon from colliding with the image when there is no title */ +} +.indexed.format-image .entry-content { + padding-top: 0.5em; +} +.indexed.format-image .entry-content p { + margin: 1em 0; +} +.indexed.format-image .entry-content p:first-child, +.indexed.format-image .entry-content p:first-child a, +.indexed.format-image .entry-content p:first-child img { + display: block; + margin: 0; +} +.indexed.format-image .entry-content .wp-caption .wp-caption-text { + margin: 0; + padding-bottom: 1em; +} +.indexed.format-image footer.entry-meta { + background: #ddd; + overflow: hidden; + padding: 4%; + max-width: 96%; +} +.indexed.format-image div.entry-meta { + display: inline-block; + float: left; + width: 35%; +} +.indexed.format-image div.entry-meta + div.entry-meta { + float: none; + width: 65%; +} +.indexed.format-image .entry-meta span.cat-links, +.indexed.format-image .entry-meta span.tag-links, +.indexed.format-image .entry-meta span.comments-link { + display: block; +} +.indexed.format-image footer.entry-meta a { + color: #444; +} +.indexed.format-image footer.entry-meta a:hover { + color: #fff; +} +#content .indexed.format-image img { + border: none; + max-width: 100%; + padding: 0; +} +.indexed.format-image .wp-caption { + background: #111; + margin-bottom: 0; + max-width: 96%; + padding: 2% 2% 0; +} +.indexed.format-image .wp-caption .wp-caption-text { + color: #ddd; +} +.indexed.format-image .wp-caption .wp-caption-text:before { + color: #444; +} +.indexed.format-image a:hover img { + opacity: 0.8; +} + + +/* =error404 +----------------------------------------------- */ + +.error404 #main #searchform { + background: #f9f9f9; + border: 1px solid #ddd; + border-width: 1px 0; + margin: 0 -8.9% 1.625em; + overflow: hidden; + padding: 1.625em 8.9%; +} +.error404 #main #s { + width: 95%; +} +.error404 #main .widget { + clear: none; + float: left; + margin-right: 3.7%; + width: 30.85%; +} +.error404 #main .widget_archive { + margin-right: 0; +} +.error404 #main .widget_tag_cloud { + float: none; + margin-right: 0; + width: 100%; +} +.error404 .widgettitle { + font-size: 10px; + letter-spacing: 0.1em; + line-height: 2.6em; + text-transform: uppercase; +} + + +/* =Showcase +----------------------------------------------- */ + +h1.showcase-heading { + color: #666; + font-size: 10px; + font-weight: 500; + letter-spacing: 0.1em; + line-height: 2.6em; + text-transform: uppercase; +} + +/* Intro */ +article.intro { + background: #f9f9f9; + border-bottom: none; + margin: -1.855em -8.9% 1.625em; + padding: 0 8.9%; +} +article.intro .entry-title { + display: none; +} +article.intro .entry-content { + color: #111; + font-size: 16px; + padding: 1.625em 0 0.625em; +} +article.intro .edit-link a { + background: #aaa; + -moz-border-radius: 3px; + border-radius: 3px; + color: #fff; + font-size: 12px; + padding: 0 8px; + position: absolute; + top: 30px; + right: 20px; + text-decoration: none; +} +article.intro .edit-link a:hover, +article.intro .edit-link a:focus, +article.intro .edit-link a:active { + background: #777; +} + +/* Featured post */ +section.featured-post { + float: left; + margin: -1.625em -8.9% 1.625em; + padding: 1.625em 8.9% 0; + position: relative; + width: 100%; +} +section.featured-post .hentry { + border: none; + color: #666; + margin: 0; +} +section.featured-post .entry-meta { + clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ + clip: rect(1px, 1px, 1px, 1px); + position: absolute !important; +} + +/* Small featured post */ +section.featured-post .attachment-small-feature { + float: right; + height: auto; + margin: 0 -8.9% 1.625em 0; + max-width: 59%; + position: relative; + right: -15px; +} +section.featured-post.small { + padding-top: 0; +} +section.featured-post .attachment-small-feature:hover, +section.featured-post .attachment-small-feature:focus, +section.featured-post .attachment-small-feature:active { + opacity: .8; +} +article.feature-image.small { + float: left; + margin: 0 0 1.625em; + width: 45%; +} +article.feature-image.small .entry-title { + line-height: 1.2em; +} +article.feature-image.small .entry-summary { + color: #555; + font-size: 13px; +} +article.feature-image.small .entry-summary p a { + background: #222; + color: #eee; + display: block; + left: -23.8%; + padding: 9px 26px 9px 85px; + position: relative; + text-decoration: none; + top: 20px; + width: 180px; + z-index: 1; +} +article.feature-image.small .entry-summary p a:hover { + background: #1982d1; + color: #eee; + color: rgba(255,255,255,0.8); +} + +/* Large featured post */ +section.feature-image.large { + border: none; + max-height: 288px; + padding: 0; + width: 100%; +} +section.feature-image.large .showcase-heading { + display: none; +} +section.feature-image.large .hentry { + border-bottom: none; + left: 9%; + margin: 1.625em 9% 0 0; + position: absolute; + top: 0; +} +article.feature-image.large .entry-title a { + background: #222; + background: rgba(0,0,0,0.8); + -moz-border-radius: 3px; + border-radius: 3px; + color: #fff; + display: inline-block; + font-weight: 300; + padding: .2em 20px; +} +section.feature-image.large:hover .entry-title a, +section.feature-image.large .entry-title:hover a { + background: #eee; + background: rgba(255,255,255,0.8); + color: #222; +} +article.feature-image.large .entry-summary { + display: none; +} +section.feature-image.large img { + display: block; + height: auto; + max-width: 117.9%; + padding: 0 0 6px; +} + +/* Featured Slider */ +.featured-posts { + border-bottom: 1px solid #ddd; + display: block; + height: 328px; + margin: 1.625em -8.9% 20px; + max-width: 1000px; + padding: 0; + position: relative; + overflow: hidden; +} +.featured-posts .showcase-heading { + padding-left: 8.9%; +} +.featured-posts section.featured-post { + background: #fff; + height: 288px; + left: 0; + margin: 0; + position: absolute; + top: 30px; + width: auto; +} +.featured-posts section.featured-post.large { + max-width: 100%; + overflow: hidden; +} +.featured-posts section.featured-post { + -webkit-transition-duration: 200ms; + -webkit-transition-property: opacity, visibility; + -webkit-transition-timing-function: ease; + -moz-transition-duration: 200ms; + -moz-transition-property: opacity, visibility; + -moz-transition-timing-function: ease; +} +.featured-posts section.featured-post { + opacity: 0; + visibility: hidden; +} +.featured-posts #featured-post-1 { + opacity: 1; + visibility: visible; +} +.featured-post .feature-text:after, +.featured-post .feature-image.small:after { + content: ' '; + background: -moz-linear-gradient(top, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,0)), color-stop(100%,rgba(255,255,255,1))); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,1) 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,1) 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,1) 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#ffffff',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,1) 100%); /* W3C */ + width: 100%; + height: 45px; + position: absolute; + top: 230px; +} +.featured-post .feature-image.small:after { + top: 253px; +} +#content .feature-slider { + top: 5px; + right: 8.9%; + overflow: visible; + position: absolute; +} +.feature-slider ul { + list-style-type: none; + margin: 0; +} +.feature-slider li { + float: left; + margin: 0 6px; +} +.feature-slider a { + background: #3c3c3c; + background: rgba(60,60,60,0.9); + -moz-border-radius: 12px; + border-radius: 12px; + -webkit-box-shadow: inset 1px 1px 5px rgba(0,0,0,0.5), inset 0 0 2px rgba(255,255,255,0.5); + -moz-box-shadow: inset 1px 1px 5px rgba(0,0,0,0.5), inset 0 0 2px rgba(255,255,255,0.5); + box-shadow: inset 1px 1px 5px rgba(0,0,0,0.5), inset 0 0 2px rgba(255,255,255,0.5); + display: block; + width: 14px; + height: 14px; +} +.feature-slider a.active { + background: #1982d1; + -webkit-box-shadow: inset 1px 1px 5px rgba(0,0,0,0.4), inset 0 0 2px rgba(255,255,255,0.8); + -moz-box-shadow: inset 1px 1px 5px rgba(0,0,0,0.4), inset 0 0 2px rgba(255,255,255,0.8); + box-shadow: inset 1px 1px 5px rgba(0,0,0,0.4), inset 0 0 2px rgba(255,255,255,0.8); + cursor: default; + opacity: 0.5; +} + +/* Recent Posts */ +section.recent-posts { + padding: 0 0 1.625em; +} +section.recent-posts .hentry { + border: none; + margin: 0; +} +section.recent-posts .other-recent-posts { + border-bottom: 1px solid #ddd; + list-style: none; + margin: 0; +} +section.recent-posts .other-recent-posts li { + padding: 0.3125em 0; + position: relative; +} +section.recent-posts .other-recent-posts .entry-title { + border-top: 1px solid #ddd; + font-size: 17px; +} +section.recent-posts .other-recent-posts a[rel="bookmark"] { + color: #373737; + float: left; + max-width: 84%; +} +section.recent-posts .other-recent-posts a[rel="bookmark"]:after { + content: '-'; + color: transparent; + font-size: 11px; +} +section.recent-posts .other-recent-posts a[rel="bookmark"]:hover { +} +section.recent-posts .other-recent-posts .comments-link a, +section.recent-posts .other-recent-posts .comments-link > span { + border-bottom: 2px solid #999; + bottom: -2px; + color: #444; + display: block; + font-size: 10px; + font-weight: 500; + line-height: 2.76333em; + padding: 0.3125em 0 0.3125em 1em; + position: absolute; + right: 0; + text-align: right; + text-transform: uppercase; + z-index: 1; +} +section.recent-posts .other-recent-posts .comments-link > span { + border-color: #bbb; + color: #888; +} +section.recent-posts .other-recent-posts .comments-link a:hover { + color: #1982d1; + border-color: #1982d1; +} +section.recent-posts .other-recent-posts li:after { + clear: both; + content: '.'; + display: block; + height: 0; + visibility: hidden; +} + + +/* =Attachments +----------------------------------------------- */ + +.image-attachment div.attachment { + background: #f9f9f9; + border: 1px solid #ddd; + border-width: 1px 0; + margin: 0 -8.9% 1.625em; + overflow: hidden; + padding: 1.625em 1.625em 0; + text-align: center; +} +.image-attachment div.attachment img { + display: block; + height: auto; + margin: 0 auto 1.625em; + max-width: 100%; +} +.image-attachment div.attachment a img { + border-color: #f9f9f9; +} +.image-attachment div.attachment a:focus img, +.image-attachment div.attachment a:hover img, +.image-attachment div.attachment a:active img { + border-color: #ddd; + background: #fff; +} +.image-attachment .entry-caption p { + font-size: 10px; + letter-spacing: 0.1em; + line-height: 2.6em; + margin: 0 0 2.6em; + text-transform: uppercase; +} + +/* =Media +-------------------------------------------------------------- */ + +audio, +video { + display: inline-block; + max-width: 100%; +} + +.attachment .entry-content .mejs-container { + margin-bottom: 24px; +} + +/* =Navigation +-------------------------------------------------------------- */ + +#content nav { + clear: both; + overflow: hidden; + padding: 0 0 1.625em; +} +#content nav a { + font-size: 12px; + font-weight: bold; + line-height: 2.2em; +} +#nav-above { + padding: 0 0 1.625em; +} +#nav-above { + display: none; +} +.paged #nav-above { + display: block; +} +.nav-previous { + float: left; + width: 50%; +} +.nav-next { + float: right; + text-align: right; + width: 50%; +} +#content nav .meta-nav { + font-weight: normal; +} + +/* Singular navigation */ +#nav-single { + float: right; + position: relative; + top: -0.3em; + text-align: right; + z-index: 1; +} +#nav-single .nav-previous, +#nav-single .nav-next { + width: auto; +} +#nav-single .nav-next { + padding-left: .5em; +} +#nav-single .nav-previous { + padding-right: .5em; +} + + +/* =Widgets +----------------------------------------------- */ + +.widget-area { + font-size: 12px; +} +.widget { + word-wrap: break-word; + -webkit-hyphens: auto; + -moz-hyphens: auto; + hyphens: auto; + clear: both; + margin: 0 0 2.2em; +} +.widget-title { + color: #666; + font-size: 10px; + font-weight: 500; + letter-spacing: 0.1em; + line-height: 2.6em; + text-transform: uppercase; +} +.widget ul { + font-size: 15px; + margin: 0; +} +.widget ul ul { + margin-left: 1.5em; +} +.widget ul li { + color: #777; + font-size: 13px; +} +.widget a { + font-weight: bold; + text-decoration: none; +} +.widget a:hover, +.widget a:focus, +.widget a:active { + text-decoration: underline; +} + +/* Search Widget */ +.widget_search form { + margin: 0 0 1.625em; +} +.widget_search #s { + width: 77%; +} +.widget_search #searchsubmit { + background: #ddd; + border: 1px solid #ccc; + -webkit-box-shadow: inset 0px -1px 1px rgba(0, 0, 0, 0.09); + -moz-box-shadow: inset 0px -1px 1px rgba(0, 0, 0, 0.09); + box-shadow: inset 0px -1px 1px rgba(0, 0, 0, 0.09); + color: #888; + font-size: 13px; + line-height: 25px; + position: relative; + top: -2px; +} +.widget_search #searchsubmit:active { + background: #1982d1; + border-color: #0861a5; + -webkit-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.1); + box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.1); + color: #bfddf3; +} + +/* Ephemera Widget */ +section.ephemera ol, +.widget_twentyeleven_ephemera ol { + list-style: square; + margin: 5px 0 0; +} +.widget_twentyeleven_ephemera .widget-entry-title { + font-size: 15px; + font-weight: bold; + padding: 0; +} +.widget_twentyeleven_ephemera .comments-link a, +.widget_twentyeleven_ephemera .comments-link > span { + color: #666; + display: block; + font-size: 10px; + font-weight: 500; + line-height: 2.76333em; + text-transform: uppercase; +} +section.ephemera .entry-title .comments-link a:hover, +.widget_twentyeleven_ephemera .entry-title .comments-link a:hover { +} +section.ephemera .entry-title a span { + color: #29628d; +} + +/* Twitter */ +.widget_twitter li { + list-style-type: none; + margin-bottom: 14px; +} +.widget_twitter .timesince { + display: block; + font-size: 11px; + margin-right: -10px; + text-align: right; +} + +/* Widget Image */ +.widget_image img { + border: 0; + padding: 0; + height: auto; + max-width: 100%; +} + +/* Calendar Widget */ + +.widget_calendar #wp-calendar { + color: #555; + width: 95%; + text-align: center; +} +.widget_calendar #wp-calendar caption, +.widget_calendar #wp-calendar td, +.widget_calendar #wp-calendar th { + text-align: center; +} +.widget_calendar #wp-calendar caption { + font-size: 11px; + font-weight: 500; + padding: 5px 0 3px 0; + text-transform: uppercase; +} +.widget_calendar #wp-calendar th { + background: #f4f4f4; + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; + font-weight: bold; +} +.widget_calendar #wp-calendar tfoot td { + background: #f4f4f4; + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; +} + + +/* =Comments +----------------------------------------------- */ + +#comments-title { + color: #666; + font-size: 10px; + font-weight: 500; + line-height: 2.6em; + padding: 0 0 2.6em; + text-transform: uppercase; +} +.nopassword, +.nocomments { + color: #aaa; + font-size: 24px; + font-weight: 100; + margin: 26px 0; + text-align: center; +} +.commentlist { + list-style: none; + margin: 0 auto; + width: 68.9%; +} +.content .commentlist, +.page-template-sidebar-page-php .commentlist { + width: 100%; /* reset the width for the one-column and sidebar page layout */ +} +.commentlist > li.comment { + background: #f6f6f6; + border: 1px solid #ddd; + -moz-border-radius: 3px; + border-radius: 3px; + margin: 0 0 1.625em; + padding: 1.625em; + position: relative; +} +.commentlist .pingback { + margin: 0 0 1.625em; + padding: 0 1.625em; +} +.commentlist .children { + list-style: none; + margin: 0; +} +.commentlist .children li.comment { + background: #fff; + border-left: 1px solid #ddd; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; + margin: 1.625em 0 0; + padding: 1.625em; + position: relative; +} +.commentlist .children li.comment .fn { + display: block; +} +.comment-meta .fn { + font-style: normal; +} +.comment-meta { + color: #666; + font-size: 12px; + line-height: 2.2em; +} +.commentlist .children li.comment .comment-meta { + line-height: 1.625em; + margin-left: 50px; +} +.commentlist .children li.comment .comment-content { + margin: 1.625em 0 0; + word-wrap: break-word; + -webkit-hyphens: auto; + -moz-hyphens: auto; + hyphens: auto; +} +.comment-meta a { + font-weight: bold; +} +.comment-meta a:focus, +.comment-meta a:active, +.comment-meta a:hover { +} +.commentlist .avatar { + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 2px #ccc; + -moz-box-shadow: 0 1px 2px #ccc; + box-shadow: 0 1px 2px #ccc; + left: -102px; + padding: 0; + position: absolute; + top: 0; +} +.commentlist > li:before { + content: url(/web/20151217170907im_/http://blog.boochtek.com/wp-content/themes/twentyeleven/images/comment-arrow.png); + left: -21px; + position: absolute; +} +.commentlist > li.pingback:before { + content: ''; +} +.commentlist .children .avatar { + background: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + left: 2.2em; + padding: 0; + top: 2.2em; +} +a.comment-reply-link { + background: #eee; + -moz-border-radius: 3px; + border-radius: 3px; + color: #666; + display: inline-block; + font-size: 12px; + padding: 0 8px; + text-decoration: none; +} +a.comment-reply-link:hover, +a.comment-reply-link:focus, +a.comment-reply-link:active { + background: #888; + color: #fff; +} +a.comment-reply-link > span { + display: inline-block; + position: relative; + top: -1px; +} + +/* Post author highlighting */ +.commentlist > li.bypostauthor { + background: #ddd; + border-color: #d3d3d3; +} +.commentlist > li.bypostauthor .comment-meta { + color: #575757; +} +.commentlist > li.bypostauthor .comment-meta a:focus, +.commentlist > li.bypostauthor .comment-meta a:active, +.commentlist > li.bypostauthor .comment-meta a:hover { +} +.commentlist > li.bypostauthor:before { + content: url(/web/20151217170907im_/http://blog.boochtek.com/wp-content/themes/twentyeleven/images/comment-arrow-bypostauthor.png); +} + +/* Post Author threaded comments */ +.commentlist .children > li.bypostauthor { + background: #ddd; + border-color: #d3d3d3; +} + +/* sidebar-page.php comments */ +/* Make sure we have room for our comment avatars */ +.page-template-sidebar-page-php .commentlist > li.comment, +.page-template-sidebar-page-php.commentlist .pingback { + margin-left: 102px; + width: auto; +} +/* And a full-width comment form */ +.page-template-sidebar-page-php #respond { + width: auto; +} + +/* Comment Form */ +#respond { + background: #ddd; + border: 1px solid #d3d3d3; + -moz-border-radius: 3px; + border-radius: 3px; + margin: 0 auto 1.625em; + padding: 1.625em; + position: relative; + width: 68.9%; +} +#respond input[type="text"], +#respond textarea { + background: #fff; + border: 4px solid #eee; + -moz-border-radius: 5px; + border-radius: 5px; + -webkit-box-shadow: inset 0 1px 3px rgba(204,204,204,0.95); + -moz-box-shadow: inset 0 1px 3px rgba(204,204,204,0.95); + box-shadow: inset 0 1px 3px rgba(204,204,204,0.95); + position: relative; + padding: 10px; + text-indent: 80px; +} +#respond .comment-form-author, +#respond .comment-form-email, +#respond .comment-form-url, +#respond .comment-form-comment { + position: relative; +} +#respond .comment-form-author label, +#respond .comment-form-email label, +#respond .comment-form-url label, +#respond .comment-form-comment label { + background: #eee; + -webkit-box-shadow: 1px 2px 2px rgba(204,204,204,0.8); + -moz-box-shadow: 1px 2px 2px rgba(204,204,204,0.8); + box-shadow: 1px 2px 2px rgba(204,204,204,0.8); + color: #555; + display: inline-block; + font-size: 13px; + left: 4px; + min-width: 60px; + padding: 4px 10px; + position: relative; + top: 40px; + z-index: 1; +} +#respond input[type="text"]:focus, +#respond textarea:focus { + text-indent: 0; + z-index: 1; +} +#respond textarea { + resize: vertical; + width: 95%; +} +#respond .comment-form-author .required, +#respond .comment-form-email .required { + color: #bd3500; + font-size: 22px; + font-weight: bold; + left: 75%; + position: absolute; + z-index: 1; +} +#respond .comment-notes, +#respond .logged-in-as { + font-size: 13px; +} +#respond p { + margin: 10px 0; +} +#respond .form-submit { + float: right; + margin: -20px 0 10px; +} +#respond input#submit { + background: #222; + border: none; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0px 1px 2px rgba(0,0,0,0.3); + -moz-box-shadow: 0px 1px 2px rgba(0,0,0,0.3); + box-shadow: 0px 1px 2px rgba(0,0,0,0.3); + color: #eee; + cursor: pointer; + font-size: 15px; + margin: 20px 0; + padding: 5px 42px 5px 22px; + position: relative; + left: 30px; + text-shadow: 0 -1px 0 rgba(0,0,0,0.3); +} +#respond input#submit:active { + background: #1982d1; + color: #bfddf3; +} +#respond #cancel-comment-reply-link { + color: #666; + margin-left: 10px; + text-decoration: none; +} +#respond .logged-in-as a:hover, +#respond #cancel-comment-reply-link:hover { + text-decoration: underline; +} +.commentlist #respond { + margin: 1.625em 0 0; + width: auto; +} +#reply-title { + color: #373737; + font-size: 24px; + font-weight: bold; + line-height: 30px; +} +#cancel-comment-reply-link { + color: #888; + display: block; + font-size: 10px; + font-weight: normal; + line-height: 2.2em; + letter-spacing: 0.05em; + position: absolute; + right: 1.625em; + text-decoration: none; + text-transform: uppercase; + top: 1.1em; +} +#cancel-comment-reply-link:focus, +#cancel-comment-reply-link:active, +#cancel-comment-reply-link:hover { + color: #ff4b33; +} +#respond label { + line-height: 2.2em; +} +#respond input[type=text] { + display: block; + height: 24px; + width: 75%; +} +#respond p { + font-size: 12px; +} +p.comment-form-comment { + margin: 0; +} +.form-allowed-tags { + display: none; +} + + +/* =Footer +----------------------------------------------- */ + +#colophon { + clear: both; +} +#supplementary { + border-top: 1px solid #ddd; + padding: 1.625em 7.6%; + overflow: hidden; +} + +/* Two Footer Widget Areas */ +#supplementary.two .widget-area { + float: left; + margin-right: 3.7%; + width: 48.1%; +} +#supplementary.two .widget-area + .widget-area { + margin-right: 0; +} + +/* Three Footer Widget Areas */ +#supplementary.three .widget-area { + float: left; + margin-right: 3.7%; + width: 30.85%; +} +#supplementary.three .widget-area + .widget-area + .widget-area { + margin-right: 0; +} + +/* Site Generator Line */ +#site-generator { + background: #f9f9f9; + border-top: 1px solid #ddd; + color: #666; + font-size: 12px; + line-height: 2.2em; + padding: 2.2em 0.5em; + text-align: center; +} +#site-generator a { + color: #555; + font-weight: bold; +} + + +/* =Responsive Structure +----------------------------------------------- */ + +/* Does the same thing as , + * but in the future W3C standard way. -ms- prefix is required for IE10+ to + * render responsive styling in Windows 8 "snapped" views; IE10+ does not honor + * the meta tag. See https://core.trac.wordpress.org/ticket/25888. + */ +@-ms-viewport { + width: device-width; +} +@viewport { + width: device-width; +} + +@media (max-width: 800px) { + /* Simplify the basic layout */ + #main #content { + margin: 0 7.6%; + width: auto; + } + #nav-below { + border-bottom: 1px solid #ddd; + margin-bottom: 1.625em; + } + #main #secondary { + float: none; + margin: 0 7.6%; + width: auto; + } + /* Simplify the showcase template */ + .page-template-showcase-php .featured-posts { + min-height: 280px; + } + .featured-posts section.featured-post { + height: auto; + } + .page-template-showcase-php section.recent-posts { + float: none; + margin: 0; + width: 100%; + } + .page-template-showcase-php #main .widget-area { + float: none; + margin: 0; + width: auto; + } + .page-template-showcase-php .other-recent-posts { + border-bottom: 1px solid #ddd; + } + /* Simplify the showcase template when small feature */ + section.featured-post .attachment-small-feature, + .one-column section.featured-post .attachment-small-feature { + border: none; + display: block; + float: left; + height: auto; + margin: 0.625em auto 1.025em; + max-width: 30%; + position: static; + } + article.feature-image.small { + float: right; + margin: 0 0 1.625em; + width: 64%; + } + .one-column article.feature-image.small .entry-summary { + height: auto; + } + article.feature-image.small .entry-summary p a { + left: 0; + padding-left: 20px; + padding-right: 20px; + width: auto; + } + /* Remove the margin on singular articles */ + .singular .entry-header, + .singular .entry-content, + .singular footer.entry-meta, + .singular #comments-title { + width: 100%; + } + /* Simplify the pullquotes and pull styles */ + .singular blockquote.pull { + margin: 0 0 1.625em; + } + .singular .pull.alignleft { + margin: 0 1.625em 0 0; + } + .singular .pull.alignright { + margin: 0 0 0 1.625em; + } + .singular .entry-meta .edit-link a { + left: 0; + position: absolute; + top: 40px; + } + .singular #author-info { + margin: 2.2em -8.8% 0; + padding: 20px 8.8%; + } + /* Make sure we have room for our comment avatars */ + .commentlist { + width: 100%; + } + .commentlist > li.comment, + .commentlist .pingback { + margin-left: 102px; + width: auto; + } + /* And a full-width comment form */ + #respond { + width: auto; + } + /* No need to float footer widgets at this size */ + #colophon #supplementary .widget-area { + float: none; + margin-right: 0; + width: auto; + } + /* No need to float 404 widgets at this size */ + .error404 #main .widget { + float: none; + margin-right: 0; + width: auto; + } +} +@media (max-width: 650px) { + /* @media (max-width: 650px) Reduce font-sizes for better readability on smaller devices */ + body, input, textarea { + font-size: 13px; + } + #site-title a { + font-size: 24px; + } + #site-description { + font-size: 12px; + } + #access ul { + font-size: 12px; + } + #branding .only-search + #access div { + padding-right: 0; + } + article.intro .entry-content { + font-size: 12px; + } + .entry-title { + font-size: 21px; + } + .featured-post .entry-title { + font-size: 14px; + } + .singular .entry-title { + font-size: 28px; + } + .entry-meta { + font-size: 12px; + } + blockquote { + margin: 0; + } + blockquote.pull { + font-size: 17px; + } + /* Reposition the site title and description slightly */ + #site-title { + padding: 5.30625em 0 0; + } + #site-title, + #site-description { + margin-right: 0; + } + /* Make sure the logo and search form don't collide */ + #branding #searchform { + top: 1.625em !important; + } + /* Floated content doesn't work well at this size */ + .alignleft, + .alignright { + display: block; + float: none; + margin-left: 0; + margin-right: 0; + } + /* Make sure the post-post navigation doesn't collide with anything */ + #nav-single { + display: block; + position: static; + } + .singular .hentry { + padding: 1.625em 0 0; + } + .singular.page .hentry { + padding: 1.625em 0 0; + } + .singular .entry-header .entry-meta, + .singular .entry-header .entry-format, + .singular .entry-meta .edit-link a { + position: static; + } + /* Talking avatars take up too much room at this size */ + .commentlist > li.comment, + .commentlist > li.pingback { + margin-left: 0 !important; + } + .commentlist .avatar { + background: transparent; + display: block; + padding: 0; + position: static; + } + .commentlist .children .avatar { + background: none; + left: 2.2em; + padding: 0; + position: absolute; + top: 2.2em; + } + /* Use the available space in the smaller comment form */ + #respond input[type="text"] { + width: 95%; + } + #respond .comment-form-author .required, + #respond .comment-form-email .required { + left: 95%; + } + #content .gallery-columns-3 .gallery-item { + width: 31%; + padding-right: 2%; + } + #content .gallery-columns-3 .gallery-item img { + width: 100%; + height: auto; + } +} +@media (max-width: 450px) { + #content .gallery-columns-2 .gallery-item { + width: 45%; + padding-right: 4%; + } + #content .gallery-columns-2 .gallery-item img { + width: 100%; + height: auto; + } +} +@media only screen and (min-device-width: 320px) and (max-device-width: 480px) { + body { + padding: 0; + } + #page { + margin-top: 0; + } + #branding { + border-top: none; + } +} + + +/* =Print +----------------------------------------------- */ + +@media print { + body { + background: none !important; + font-size: 10pt; + } + footer.entry-meta a[rel=bookmark]:link:after, + footer.entry-meta a[rel=bookmark]:visited:after { + content: " [" attr(href) "] "; /* Show URLs */ + } + #page { + clear: both !important; + display: block !important; + float: none !important; + max-width: 100%; + position: relative !important; + } + #branding { + border-top: none !important; + padding: 0; + } + #branding hgroup { + margin: 0; + } + #site-title a { + font-size: 21pt; + } + #site-description { + font-size: 10pt; + } + #branding #searchform { + display: none; + } + #branding img { + display: none; + } + #access { + display: none; + } + #main { + border-top: none; + box-shadow: none; + } + #primary { + float: left; + margin: 0; + width: 100%; + } + #content { + margin: 0; + width: auto; + } + .singular #content { + margin: 0; + width: 100%; + } + .singular .entry-header .entry-meta { + position: static; + } + .entry-meta .edit-link a { + display: none; + } + #content nav { + display: none; + } + .singular .entry-header, + .singular .entry-content, + .singular footer.entry-meta, + .singular #comments-title { + margin: 0; + width: 100%; + } + .singular .hentry { + padding: 0; + } + .entry-title, + .singular .entry-title { + font-size: 21pt; + } + .entry-meta { + font-size: 10pt; + } + .entry-header .comments-link { + display: none; + } + .page-link { + display: none; + } + .singular #author-info { + background: none; + border-bottom: none; + border-top: none; + margin: 2.2em 0 0; + padding: 0; + } + #respond { + display: none; + } + .widget-area { + display: none; + } + #colophon { + display: none; + } + + /* Comments */ + .commentlist > li.comment { + background: none; + border: 1px solid #ddd; + -moz-border-radius: 3px 3px 3px 3px; + border-radius: 3px 3px 3px 3px; + margin: 0 auto 1.625em; + padding: 1.625em; + position: relative; + width: auto; + } + .commentlist .avatar { + height: 39px; + left: 2.2em; + top: 2.2em; + width: 39px; + } + .commentlist li.comment .comment-meta { + line-height: 1.625em; + margin-left: 50px; + } + .commentlist li.comment .fn { + display: block; + } + .commentlist li.comment .comment-content { + margin: 1.625em 0 0; + } + .commentlist .comment-edit-link { + display: none; + } + .commentlist > li::before, + .commentlist > li.bypostauthor::before { + content: ''; + } + .commentlist .reply { + display: none; + } + + /* Post author highlighting */ + .commentlist > li.bypostauthor { + color: #444; + } + .commentlist > li.bypostauthor .comment-meta { + color: #666; + } + .commentlist > li.bypostauthor:before { + content: none; + } + + /* Post Author threaded comments */ + .commentlist .children > li.bypostauthor { + background: #fff; + border-color: #ddd; + } + .commentlist .children > li.bypostauthor > article, + .commentlist .children > li.bypostauthor > article .comment-meta { + color: #666; + } +} + + +/* =IE7 +----------------------------------------------- */ + +#ie7 article.intro { + margin-left: -7.6%; + margin-right: -7.6%; + padding-left: -7.6%; + padding-right: -7.6%; + max-width: 1000px; +} +#ie7 .featured-posts { + margin: 0 -7.6%; +} +#ie7 .featured-post { + margin-left: 0; + margin-right: 0; + max-width: 100%; +} +#ie7 section.recent-posts { + margin-right: 7.6%; +} + + +/* =IE8 +----------------------------------------------- */ + +#ie8 section.feature-image.large img { + width: auto; +} +#ie8 section.featured-post .attachment-small-feature { + max-width: none; +} + +/* + FILE ARCHIVED ON 17:09:07 Dec 17, 2015 AND RETRIEVED FROM THE + INTERNET ARCHIVE ON 08:36:57 Feb 23, 2024. + JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. + + ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. + SECTION 108(a)(3)). +*/ +/* +playback timings (ms): + exclusion.robots: 0.163 + exclusion.robots.policy: 0.148 + cdx.remote: 0.119 + esindex: 0.013 + LoadShardBlock: 344.18 (6) + PetaboxLoader3.datanode: 119.631 (7) + load_resource: 111.921 + PetaboxLoader3.resolve: 79.978 +*/ \ No newline at end of file diff --git a/public/wp-content/themes/twentysixteen/css/blocks.css b/public/wp-content/themes/twentysixteen/css/blocks.css new file mode 100644 index 0000000..fb0a295 --- /dev/null +++ b/public/wp-content/themes/twentysixteen/css/blocks.css @@ -0,0 +1,474 @@ +/* +Theme Name: Twenty Sixteen +Description: Used to style blocks. +*/ + +/*-------------------------------------------------------------- +>>> TABLE OF CONTENTS: +---------------------------------------------------------------- +1.0 General Block Styles +2.0 Blocks - Common Blocks +3.0 Blocks - Formatting +4.0 Blocks - Layout Elements +5.0 Blocks - Widgets +6.0 Blocks - Colors +--------------------------------------------------------------*/ + +/*-------------------------------------------------------------- +1.0 General Block Styles +--------------------------------------------------------------*/ + +/* Captions */ + +[class^="wp-block-"] figcaption { + color: #686868; + font-style: italic; + line-height: 1.6153846154; + padding-top: 0.5384615385em; + text-align: left; +} + +.rtl [class^="wp-block-"] figcaption { + text-align: right; +} + +/*-------------------------------------------------------------- +2.0 Blocks - Common Blocks +--------------------------------------------------------------*/ + +/* Paragraph */ + +p.has-drop-cap:not(:focus)::first-letter { + font-size: 5em; +} + +/* Image */ + +@media screen and (min-width: 61.5625em) { + body:not(.search-results) article:not(.type-page) .wp-block-image figcaption.below-entry-meta { + clear: both; + display: block; + float: none; + margin-right: 0; + margin-left: -40%; + max-width: 140%; + } + + body.rtl:not(.search-results) article:not(.type-page) .wp-block-image figcaption.below-entry-meta { + margin-left: 0; + margin-right: -40%; + } +} + +/* Gallery */ + +.wp-block-gallery { + margin-bottom: 1.75em; +} + +/* Quote */ + +.wp-block-quote:not(.is-large):not(.is-style-large).alignleft, +.wp-block-quote:not(.is-large):not(.is-style-large).alignright { + border-left: none; + padding-left: 0; +} + +.rtl .wp-block-quote:not(.is-large):not(.is-style-large).alignleft, +.rtl .wp-block-quote:not(.is-large):not(.is-style-large).alignright { + border-right: none; + padding-right: 0; +} + +.wp-block-quote cite { + color: #1a1a1a; + display: block; + font-size: 16px; + font-size: 1rem; + line-height: 1.75; +} + +.wp-block-quote cite:before { + content: "\2014\00a0"; +} + +/* Audio */ + +.wp-block-audio audio { + display: block; + width: 100%; +} + +/* Cover */ + +.wp-block-cover-image.aligncenter, +.wp-block-cover.aligncenter { + display: flex; +} + +/* File */ + +.wp-block-file .wp-block-file__button { + background: #1a1a1a; + border: 0; + border-radius: 2px; + color: #fff; + font-family: Montserrat, "Helvetica Neue", sans-serif; + font-weight: 700; + letter-spacing: 0.046875em; + line-height: 1; + padding: 0.84375em 0.875em 0.78125em; + text-transform: uppercase; +} + +.wp-block-file .wp-block-file__button:hover, +.wp-block-file .wp-block-file__button:focus { + background: #007acc; +} + +.wp-block-file .wp-block-file__button:focus { + outline: thin dotted; + outline-offset: -4px; +} + +.rtl .wp-block-file * + .wp-block-file__button { + margin-left: 0.75em; + margin-right: 0; +} + +/*-------------------------------------------------------------- +3.0 Blocks - Formatting Blocks +--------------------------------------------------------------*/ + +/* Code */ + +.wp-block-code { + border: 0; + font-family: Inconsolata, monospace; + line-height: 1.75; + padding: 0; +} + +.wp-block-code code { + font-size: inherit; +} + +/* Pullquote */ + +.wp-block-pullquote { + border-width: 4px; +} + +.wp-block-pullquote blockquote { + color: #686868; + border-left: 4px solid #1a1a1a; + margin: 0; + padding: 0 0 0 24px; +} + +.rtl .wp-block-pullquote blockquote { + border-left: none; + border-right: 4px solid #1a1a1a; + padding: 0 24px 0 0; +} + +.wp-block-pullquote p { + font-size: 19px; + font-size: 1.1875rem; +} + +.wp-block-pullquote cite { + color: #1a1a1a; + display: block; + font-size: 16px; + font-size: 1rem; + font-style: none; + line-height: 1.75; + text-transform: none; +} + +.wp-block-pullquote cite:before { + content: "\2014\00a0"; +} + +/* Table */ + +.wp-block-table, +.wp-block-table th, +.wp-block-table td { + border: 1px solid #d1d1d1; +} + +.wp-block-table { + border-collapse: separate; + border-spacing: 0; + border-width: 0; + margin: 0 0 1.75em; + table-layout: fixed; + width: 100%; +} + +.wp-block-table th, +.wp-block-table td { + font-weight: normal; + padding: 0.4375em; + text-align: left; +} + +.wp-block-table th { + border-width: 0 1px 1px 0; + font-weight: 700; +} + +.wp-block-table td { + border-width: 0 1px 1px 0; +} + +.rtl .wp-block-table th, +.rtl .wp-block-table td { + text-align: right; +} + +/*-------------------------------------------------------------- +4.0 Blocks - Layout Elements +--------------------------------------------------------------*/ + +/* Buttons */ + +.wp-block-button .wp-block-button__link { + box-shadow: none; + font-family: Montserrat, "Helvetica Neue", sans-serif; + font-weight: 700; + letter-spacing: 0.046875em; + line-height: 1; + padding: 0.84375em 1.3125em 0.78125em; + text-transform: uppercase; +} + +.entry-content .wp-block-button__link { + background: #1a1a1a; + color: #fff; +} + +.entry-content .is-style-outline .wp-block-button__link:not(.has-background) { + background: transparent; +} + +.entry-content .is-style-outline .wp-block-button__link:not(.has-text-color) { + color: #1a1a1a; +} + +.entry-content .wp-block-button__link:hover, +.entry-content .wp-block-button__link:focus, +.entry-content .is-style-outline .wp-block-button__link:not(.has-background):hover, +.entry-content .is-style-outline .wp-block-button__link:not(.has-background):focus, +.entry-content .is-style-outline .wp-block-button__link:not(.has-text-color):hover, +.entry-content .is-style-outline .wp-block-button__link:not(.has-text-color):focus { + background: #007acc; + color: #fff; +} + +.wp-block-button .wp-block-button__link:focus { + outline: thin dotted; + outline-offset: -4px; +} + +/* Seperator */ + +hr.wp-block-separator { + border: 0; +} + +.wp-block-separator { + margin-left: auto; + margin-right: auto; + max-width: 100px; +} + +.wp-block-separator.is-style-wide { + max-width: 100%; +} + +/* Media & Text */ + +.wp-block-media-text { + margin-bottom: 1.75em; +} + +.wp-block-media-text *:last-child { + margin-bottom: 0; +} + +/*-------------------------------------------------------------- +5.0 Blocks - Widget Blocks +--------------------------------------------------------------*/ + +/* Archives, Categories & Latest Posts */ + +.wp-block-archives.aligncenter, +.wp-block-categories.aligncenter, +.wp-block-latest-posts.aligncenter { + list-style-position: inside; + text-align: center; +} + +/* Latest Comments */ + +.wp-block-latest-comments__comment-meta a { + box-shadow: none; + font-weight: 700; +} + +.wp-block-latest-comments__comment-date { + color: #686868; + font-family: Montserrat, "Helvetica Neue", sans-serif; + font-size: 13px; + font-size: 0.8125rem; + line-height: 1.6153846154; +} + +.wp-block-latest-comments .wp-block-latest-comments__comment:not(:first-child) { + border-top: 1px solid #d1d1d1; + margin-bottom: 0; + padding: 1.75em 0; +} + +.wp-block-latest-comments__comment-excerpt p:last-child { + margin-bottom: 0; +} + +/* Query Loop & Post Template */ + +.wp-block-query .wp-block-post-template { + margin-left: 0; +} + +.wp-block-query .wp-block-post-template li { + margin-bottom: 0; + padding: 1.5em 0 0; +} + +.wp-block-query .wp-block-post-template li:not(:first-child) { + border-top: 1px solid #d1d1d1; +} + +/*-------------------------------------------------------------- +6.0 Blocks - Colors +--------------------------------------------------------------*/ + +.entry-content .has-dark-gray-color { + color: #1a1a1a; +} + +.entry-content .has-dark-gray-background-color { + background-color: #1a1a1a; +} + +.entry-content .has-medium-gray-color { + color: #686868; +} + +.entry-content .has-medium-gray-background-color { + background-color: #686868; +} + +.entry-content .has-light-gray-color { + color: #e5e5e5; +} + +.entry-content .has-light-gray-background-color { + background-color: #e5e5e5; +} + +.entry-content .has-white-color { + color: #fff; +} + +.entry-content .has-white-background-color { + background-color: #fff; +} + +.entry-content .has-blue-gray-color { + color: #4d545c; +} + +.entry-content .has-blue-gray-background-color { + background-color: #4d545c; +} + +.entry-content .has-bright-blue-color { + color: #007acc; +} + +.entry-content .has-bright-blue-background-color { + background-color: #007acc; +} + +.entry-content .has-light-blue-color { + color: #9adffd; +} + +.entry-content .has-light-blue-background-color { + background-color: #9adffd; +} + +.entry-content .has-dark-brown-color { + color: #402b30; +} + +.entry-content .has-dark-brown-background-color { + background-color: #402b30; +} + +.entry-content .has-medium-brown-color { + color: #774e24; +} + +.entry-content .has-medium-brown-background-color { + background-color: #774e24; +} + +.entry-content .has-dark-red-color { + color: #640c1f; +} + +.entry-content .has-dark-red-background-color { + background-color: #640c1f; +} + +.entry-content .has-bright-red-color { + color: #ff675f; +} + +.entry-content .has-bright-red-background-color { + background-color: #ff675f; +} + +.entry-content .has-yellow-color { + color: #ffef8e; +} + +.entry-content .has-yellow-background-color { + background-color: #ffef8e; +} + +/* + FILE ARCHIVED ON 08:03:46 Dec 06, 2022 AND RETRIEVED FROM THE + INTERNET ARCHIVE ON 08:37:07 Feb 23, 2024. + JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. + + ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. + SECTION 108(a)(3)). +*/ +/* +playback timings (ms): + exclusion.robots: 0.131 + exclusion.robots.policy: 0.121 + cdx.remote: 0.11 + esindex: 0.01 + LoadShardBlock: 96.82 (6) + PetaboxLoader3.datanode: 112.29 (8) + load_resource: 158.88 + PetaboxLoader3.resolve: 107.741 + loaddict: 36.376 +*/ \ No newline at end of file diff --git a/public/wp-content/themes/twentysixteen/css/blocks.css?ver=20190102.css b/public/wp-content/themes/twentysixteen/css/blocks.css?ver=20190102.css new file mode 100644 index 0000000..c658da8 --- /dev/null +++ b/public/wp-content/themes/twentysixteen/css/blocks.css?ver=20190102.css @@ -0,0 +1,474 @@ +/* +Theme Name: Twenty Sixteen +Description: Used to style blocks. +*/ + +/*-------------------------------------------------------------- +>>> TABLE OF CONTENTS: +---------------------------------------------------------------- +1.0 General Block Styles +2.0 Blocks - Common Blocks +3.0 Blocks - Formatting +4.0 Blocks - Layout Elements +5.0 Blocks - Widgets +6.0 Blocks - Colors +--------------------------------------------------------------*/ + +/*-------------------------------------------------------------- +1.0 General Block Styles +--------------------------------------------------------------*/ + +/* Captions */ + +[class^="wp-block-"] figcaption { + color: #686868; + font-style: italic; + line-height: 1.6153846154; + padding-top: 0.5384615385em; + text-align: left; +} + +.rtl [class^="wp-block-"] figcaption { + text-align: right; +} + +/*-------------------------------------------------------------- +2.0 Blocks - Common Blocks +--------------------------------------------------------------*/ + +/* Paragraph */ + +p.has-drop-cap:not(:focus)::first-letter { + font-size: 5em; +} + +/* Image */ + +@media screen and (min-width: 61.5625em) { + body:not(.search-results) article:not(.type-page) .wp-block-image figcaption.below-entry-meta { + clear: both; + display: block; + float: none; + margin-right: 0; + margin-left: -40%; + max-width: 140%; + } + + body.rtl:not(.search-results) article:not(.type-page) .wp-block-image figcaption.below-entry-meta { + margin-left: 0; + margin-right: -40%; + } +} + +/* Gallery */ + +.wp-block-gallery { + margin-bottom: 1.75em; +} + +/* Quote */ + +.wp-block-quote:not(.is-large):not(.is-style-large).alignleft, +.wp-block-quote:not(.is-large):not(.is-style-large).alignright { + border-left: none; + padding-left: 0; +} + +.rtl .wp-block-quote:not(.is-large):not(.is-style-large).alignleft, +.rtl .wp-block-quote:not(.is-large):not(.is-style-large).alignright { + border-right: none; + padding-right: 0; +} + +.wp-block-quote cite { + color: #1a1a1a; + display: block; + font-size: 16px; + font-size: 1rem; + line-height: 1.75; +} + +.wp-block-quote cite:before { + content: "\2014\00a0"; +} + +/* Audio */ + +.wp-block-audio audio { + display: block; + width: 100%; +} + +/* Cover */ + +.wp-block-cover-image.aligncenter, +.wp-block-cover.aligncenter { + display: flex; +} + +/* File */ + +.wp-block-file .wp-block-file__button { + background: #1a1a1a; + border: 0; + border-radius: 2px; + color: #fff; + font-family: Montserrat, "Helvetica Neue", sans-serif; + font-weight: 700; + letter-spacing: 0.046875em; + line-height: 1; + padding: 0.84375em 0.875em 0.78125em; + text-transform: uppercase; +} + +.wp-block-file .wp-block-file__button:hover, +.wp-block-file .wp-block-file__button:focus { + background: #007acc; +} + +.wp-block-file .wp-block-file__button:focus { + outline: thin dotted; + outline-offset: -4px; +} + +.rtl .wp-block-file * + .wp-block-file__button { + margin-left: 0.75em; + margin-right: 0; +} + +/*-------------------------------------------------------------- +3.0 Blocks - Formatting Blocks +--------------------------------------------------------------*/ + +/* Code */ + +.wp-block-code { + border: 0; + font-family: Inconsolata, monospace; + line-height: 1.75; + padding: 0; +} + +.wp-block-code code { + font-size: inherit; +} + +/* Pullquote */ + +.wp-block-pullquote { + border-width: 4px; +} + +.wp-block-pullquote blockquote { + color: #686868; + border-left: 4px solid #1a1a1a; + margin: 0; + padding: 0 0 0 24px; +} + +.rtl .wp-block-pullquote blockquote { + border-left: none; + border-right: 4px solid #1a1a1a; + padding: 0 24px 0 0; +} + +.wp-block-pullquote p { + font-size: 19px; + font-size: 1.1875rem; +} + +.wp-block-pullquote cite { + color: #1a1a1a; + display: block; + font-size: 16px; + font-size: 1rem; + font-style: none; + line-height: 1.75; + text-transform: none; +} + +.wp-block-pullquote cite:before { + content: "\2014\00a0"; +} + +/* Table */ + +.wp-block-table, +.wp-block-table th, +.wp-block-table td { + border: 1px solid #d1d1d1; +} + +.wp-block-table { + border-collapse: separate; + border-spacing: 0; + border-width: 0; + margin: 0 0 1.75em; + table-layout: fixed; + width: 100%; +} + +.wp-block-table th, +.wp-block-table td { + font-weight: normal; + padding: 0.4375em; + text-align: left; +} + +.wp-block-table th { + border-width: 0 1px 1px 0; + font-weight: 700; +} + +.wp-block-table td { + border-width: 0 1px 1px 0; +} + +.rtl .wp-block-table th, +.rtl .wp-block-table td { + text-align: right; +} + +/*-------------------------------------------------------------- +4.0 Blocks - Layout Elements +--------------------------------------------------------------*/ + +/* Buttons */ + +.wp-block-button .wp-block-button__link { + box-shadow: none; + font-family: Montserrat, "Helvetica Neue", sans-serif; + font-weight: 700; + letter-spacing: 0.046875em; + line-height: 1; + padding: 0.84375em 1.3125em 0.78125em; + text-transform: uppercase; +} + +.entry-content .wp-block-button__link { + background: #1a1a1a; + color: #fff; +} + +.entry-content .is-style-outline .wp-block-button__link:not(.has-background) { + background: transparent; +} + +.entry-content .is-style-outline .wp-block-button__link:not(.has-text-color) { + color: #1a1a1a; +} + +.entry-content .wp-block-button__link:hover, +.entry-content .wp-block-button__link:focus, +.entry-content .is-style-outline .wp-block-button__link:not(.has-background):hover, +.entry-content .is-style-outline .wp-block-button__link:not(.has-background):focus, +.entry-content .is-style-outline .wp-block-button__link:not(.has-text-color):hover, +.entry-content .is-style-outline .wp-block-button__link:not(.has-text-color):focus { + background: #007acc; + color: #fff; +} + +.wp-block-button .wp-block-button__link:focus { + outline: thin dotted; + outline-offset: -4px; +} + +/* Seperator */ + +hr.wp-block-separator { + border: 0; +} + +.wp-block-separator { + margin-left: auto; + margin-right: auto; + max-width: 100px; +} + +.wp-block-separator.is-style-wide { + max-width: 100%; +} + +/* Media & Text */ + +.wp-block-media-text { + margin-bottom: 1.75em; +} + +.wp-block-media-text *:last-child { + margin-bottom: 0; +} + +/*-------------------------------------------------------------- +5.0 Blocks - Widget Blocks +--------------------------------------------------------------*/ + +/* Archives, Categories & Latest Posts */ + +.wp-block-archives.aligncenter, +.wp-block-categories.aligncenter, +.wp-block-latest-posts.aligncenter { + list-style-position: inside; + text-align: center; +} + +/* Latest Comments */ + +.wp-block-latest-comments__comment-meta a { + box-shadow: none; + font-weight: 700; +} + +.wp-block-latest-comments__comment-date { + color: #686868; + font-family: Montserrat, "Helvetica Neue", sans-serif; + font-size: 13px; + font-size: 0.8125rem; + line-height: 1.6153846154; +} + +.wp-block-latest-comments .wp-block-latest-comments__comment:not(:first-child) { + border-top: 1px solid #d1d1d1; + margin-bottom: 0; + padding: 1.75em 0; +} + +.wp-block-latest-comments__comment-excerpt p:last-child { + margin-bottom: 0; +} + +/* Query Loop & Post Template */ + +.wp-block-query .wp-block-post-template { + margin-left: 0; +} + +.wp-block-query .wp-block-post-template li { + margin-bottom: 0; + padding: 1.5em 0 0; +} + +.wp-block-query .wp-block-post-template li:not(:first-child) { + border-top: 1px solid #d1d1d1; +} + +/*-------------------------------------------------------------- +6.0 Blocks - Colors +--------------------------------------------------------------*/ + +.entry-content .has-dark-gray-color { + color: #1a1a1a; +} + +.entry-content .has-dark-gray-background-color { + background-color: #1a1a1a; +} + +.entry-content .has-medium-gray-color { + color: #686868; +} + +.entry-content .has-medium-gray-background-color { + background-color: #686868; +} + +.entry-content .has-light-gray-color { + color: #e5e5e5; +} + +.entry-content .has-light-gray-background-color { + background-color: #e5e5e5; +} + +.entry-content .has-white-color { + color: #fff; +} + +.entry-content .has-white-background-color { + background-color: #fff; +} + +.entry-content .has-blue-gray-color { + color: #4d545c; +} + +.entry-content .has-blue-gray-background-color { + background-color: #4d545c; +} + +.entry-content .has-bright-blue-color { + color: #007acc; +} + +.entry-content .has-bright-blue-background-color { + background-color: #007acc; +} + +.entry-content .has-light-blue-color { + color: #9adffd; +} + +.entry-content .has-light-blue-background-color { + background-color: #9adffd; +} + +.entry-content .has-dark-brown-color { + color: #402b30; +} + +.entry-content .has-dark-brown-background-color { + background-color: #402b30; +} + +.entry-content .has-medium-brown-color { + color: #774e24; +} + +.entry-content .has-medium-brown-background-color { + background-color: #774e24; +} + +.entry-content .has-dark-red-color { + color: #640c1f; +} + +.entry-content .has-dark-red-background-color { + background-color: #640c1f; +} + +.entry-content .has-bright-red-color { + color: #ff675f; +} + +.entry-content .has-bright-red-background-color { + background-color: #ff675f; +} + +.entry-content .has-yellow-color { + color: #ffef8e; +} + +.entry-content .has-yellow-background-color { + background-color: #ffef8e; +} + +/* + FILE ARCHIVED ON 00:32:22 Dec 06, 2022 AND RETRIEVED FROM THE + INTERNET ARCHIVE ON 08:37:16 Feb 23, 2024. + JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. + + ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. + SECTION 108(a)(3)). +*/ +/* +playback timings (ms): + exclusion.robots: 0.103 + exclusion.robots.policy: 0.091 + cdx.remote: 0.17 + esindex: 0.011 + LoadShardBlock: 508.864 (6) + PetaboxLoader3.datanode: 286.593 (8) + load_resource: 191.495 + PetaboxLoader3.resolve: 57.851 + loaddict: 101.945 +*/ \ No newline at end of file diff --git a/public/wp-content/themes/twentysixteen/css/ie.css?ver=20150825.css b/public/wp-content/themes/twentysixteen/css/ie.css?ver=20150825.css new file mode 100644 index 0000000..d9822c6 --- /dev/null +++ b/public/wp-content/themes/twentysixteen/css/ie.css?ver=20150825.css @@ -0,0 +1,68 @@ +/* +Theme Name: Twenty Sixteen +Description: Global Styles for older IE versions (previous to IE10). +*/ + +.site-header-main:before, +.site-header-main:after, +.site-footer:before, +.site-footer:after { + content: ""; + display: table; +} + +.site-header-main:after, +.site-footer:after { + clear: both; +} + +@media screen and (min-width: 56.875em) { + .site-branding, + .site-info { + float: left; + } + + .site-header-menu, + .site-footer .social-navigation { + float: right; + } + + .site-footer .social-navigation { + margin-left: 7px; + } + + .rtl .site-branding, + .rtl .site-info { + float: right; + } + + .rtl .site-header-menu, + .rtl .site-footer .social-navigation { + float: left; + } + + .rtl .site-footer .social-navigation { + margin-right: 7px; + margin-left: 0; + } +} + +/* + FILE ARCHIVED ON 22:01:51 May 28, 2022 AND RETRIEVED FROM THE + INTERNET ARCHIVE ON 08:37:26 Feb 23, 2024. + JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. + + ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. + SECTION 108(a)(3)). +*/ +/* +playback timings (ms): + exclusion.robots: 0.121 + exclusion.robots.policy: 0.109 + cdx.remote: 0.094 + esindex: 0.01 + LoadShardBlock: 98.211 (6) + PetaboxLoader3.datanode: 108.645 (7) + load_resource: 99.831 + PetaboxLoader3.resolve: 45.089 +*/ \ No newline at end of file diff --git a/public/wp-content/themes/twentysixteen/css/ie7.css?ver=20150825.css b/public/wp-content/themes/twentysixteen/css/ie7.css?ver=20150825.css new file mode 100644 index 0000000..b8b1fdd --- /dev/null +++ b/public/wp-content/themes/twentysixteen/css/ie7.css?ver=20150825.css @@ -0,0 +1,196 @@ +/* +Theme Name: Twenty Sixteen +Description: IE7 specific style. +*/ + +.site-inner { + max-width: 656px; +} + +.post-navigation, +.pagination, +.image-navigation, +.entry-header, +.entry-summary, +.entry-content, +.entry-footer, +.page-header, +.page-content, +.post-thumbnail, +.content-bottom-widgets, +.comments-area { + margin-right: 28px; + margin-left: 28px; + max-width: 100%; +} + +.site-header, +.sidebar, +.site-footer, +.widecolumn { + padding-right: 28px; + padding-left: 28px; +} + +.search-submit { + height: auto; + margin-top: 28px; + padding: 15px 0 8px; + position: relative; + width: auto; +} + +.search-submit .screen-reader-text { + height: auto; + position: relative !important; + width: auto; +} + +.image-navigation .nav-previous, +.image-navigation .nav-next, +.comment-navigation .nav-previous, +.comment-navigation .nav-next { + *display: inline; + zoom: 1; +} + +.image-navigation .nav-previous + .nav-next, +.comment-navigation .nav-previous + .nav-next { + margin-left: 14px; +} + +.pagination .nav-links { + padding: 0; +} + +.pagination .page-numbers { + line-height: 1; + margin: -4px 14px 0; + padding: 18px 0; +} + +.pagination .prev, +.pagination .next { + display: inline-block; + font-size: 16px; + font-weight: 700; + height: auto; + left: 0; + line-height: 1; + margin: 0; + padding: 18px 14px; + position: relative; + right: 0; + text-transform: none; + width: auto; +} + +.dropdown-toggle { + display: none; +} + +.main-navigation ul ul { + display: block; +} + +.social-navigation { + margin-top: 1.75em; +} + +.social-navigation a { + height: auto; + padding: 3px 7px; + width: auto; +} + +.social-navigation .screen-reader-text { + height: auto; + position: relative !important; + width: auto; +} + +.site-header-main { + overflow : hidden; + zoom : 1; +} + +.entry-footer > span { + margin-right: 14px; +} + +.site-info .site-title { + font-size: 13px; + margin-right: 14px; +} + +.gallery-item { + max-width: 30%; +} + +.gallery-columns-1 .gallery-item { + max-width: 100%; +} + +.gallery-columns-2 .gallery-item { + max-width: 46%; +} + +.gallery-columns-4 .gallery-item { + max-width: 22%; +} + +.gallery-columns-5 .gallery-item { + max-width: 17%; +} + +.gallery-columns-6 .gallery-item { + max-width: 13.5%; +} + +.gallery-columns-7 .gallery-item { + max-width: 11%; +} + +.gallery-columns-8 .gallery-item { + max-width: 9.5%; +} + +.gallery-columns-9 .gallery-item { + max-width: 8%; +} + +.rtl .image-navigation .nav-previous + .nav-next, +.rtl .comment-navigation .nav-previous + .nav-next { + margin-right: 14px; + margin-left: 0; +} + +.rtl .entry-footer > span { + margin-right: 14px; + margin-left: 0; +} + +.rtl .site-info .site-title { + margin-right: 0; + margin-left: 14px; +} + +/* + FILE ARCHIVED ON 03:58:14 Mar 09, 2022 AND RETRIEVED FROM THE + INTERNET ARCHIVE ON 08:37:42 Feb 23, 2024. + JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. + + ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. + SECTION 108(a)(3)). +*/ +/* +playback timings (ms): + exclusion.robots: 0.181 + exclusion.robots.policy: 0.162 + cdx.remote: 0.175 + esindex: 0.015 + LoadShardBlock: 181.247 (6) + PetaboxLoader3.datanode: 149.285 (7) + load_resource: 7604.571 + PetaboxLoader3.resolve: 7553.346 +*/ \ No newline at end of file diff --git a/public/wp-content/themes/twentysixteen/css/ie8.css?ver=20150825.css b/public/wp-content/themes/twentysixteen/css/ie8.css?ver=20150825.css new file mode 100644 index 0000000..e09b894 --- /dev/null +++ b/public/wp-content/themes/twentysixteen/css/ie8.css?ver=20150825.css @@ -0,0 +1,242 @@ +/* +Theme Name: Twenty Sixteen +Description: IE8 specific style. +*/ + +code { + background-color: transparent; + padding: 0; +} + +.entry-content a, +.entry-summary a, +.taxonomy-description a, +.logged-in-as a, +.comment-content a, +.pingback .comment-body > a, +.textwidget a, +.entry-footer a:hover, +.site-info a:hover { + text-decoration: underline; +} + +.entry-content a:hover, +.entry-content a:focus, +.entry-summary a:hover, +.entry-summary a:focus, +.taxonomy-description a:hover, +.taxonomy-description a:focus, +.logged-in-as a:hover, +.logged-in-as a:focus, +.comment-content a:hover, +.comment-content a:focus, +.pingback .comment-body > a:hover, +.pingback .comment-body > a:focus, +.textwidget a:hover, +.textwidget a:focus, +.entry-content .wp-audio-shortcode a, +.entry-content .wp-playlist a, +.page-links a { + text-decoration: none; +} + +.site { + margin: 21px; +} + +.site-inner { + max-width: 710px; +} + +.site-header { + padding-top: 3.9375em; + padding-bottom: 3.9375em; +} + +.site-branding { + float: left; + margin-top: 1.3125em; + margin-bottom: 1.3125em; +} + +.site-title { + font-size: 28px; + line-height: 1.25; +} + +.site-description { + display: block; +} + +.menu-toggle { + float: right; + font-size: 16px; + margin: 1.3125em 0; + padding: 0.8125em 0.875em 0.6875em; +} + +.site-header-menu { + clear: both; + margin: 0; + padding: 1.3125em 0; +} + +.site-header .main-navigation + .social-navigation { + margin-top: 2.625em; +} + +.header-image { + margin: 1.3125em 0; +} + +.site-main { + margin-bottom: 5.25em; +} + +.post-navigation { + margin-bottom: 5.25em; +} + +.post-navigation .post-title { + font-size: 28px; + line-height: 1.25; +} + +.pagination { + margin: 0 7.6923% 4.421052632em; +} + +.pagination .nav-links:before, +.pagination .nav-links:after { + display: none; +} + +/* restore screen-reader-text */ +.pagination .current .screen-reader-text { + position: absolute !important; +} + +.pagination .page-numbers { + display: inline-block; + font-weight: 400; +} + +.image-navigation .nav-previous, +.image-navigation .nav-next, +.comment-navigation .nav-previous, +.comment-navigation .nav-next { + display: inline-block; +} + +.image-navigation .nav-previous + .nav-next:before, +.comment-navigation .nav-previous + .nav-next:before { + content: "\002f"; + display: inline-block; + filter: alpha(opacity=70); + padding: 0 0.538461538em; +} + +.site-main > article { + margin-bottom: 5.25em; +} + +.entry-title { + font-size: 33px; + line-height: 1.2727272727; + margin-bottom: 0.8484848485em; +} + +.entry-content blockquote.alignleft, +.entry-content blockquote.alignright { + border-width: 4px 0 0 0; + padding: 0.9473684211em 0 0; + width: 50%; +} + +.entry-footer > span:after { + content: "\002f"; + display: inline-block; + filter: alpha(opacity=70); + padding: 0 0.538461538em; +} + +.updated { + display: none; +} + +.updated.published { + display: inline; +} + +.comment-author { + margin-bottom: 0; +} + +.comment-author .avatar { + height: 42px; + position: relative; + top: 0.25em; + width: 42px; +} + +.comment-list .children > li { + padding-left: 1.75em; +} + +.comment-list + .comment-respond, +.comment-navigation + .comment-respond { + padding-top: 3.5em; +} + +.comment-reply-link { + margin-top: 0; +} + +.comments-area, +.widget, +.content-bottom-widgets .widget-area { + margin-bottom: 5.25em; +} + +.sidebar, +.widecolumn { + margin-bottom: 5.25em; +} + +.site-footer .main-navigation, +.site-footer .social-navigation { + display: none; +} + +.rtl .site-branding { + float: right; +} + +.rtl .menu-toggle { + float: left; +} + +.rtl .comment-list .children > li { + padding-right: 1.75em; + padding-left: 0; +} + +/* + FILE ARCHIVED ON 11:26:17 Apr 11, 2022 AND RETRIEVED FROM THE + INTERNET ARCHIVE ON 08:38:00 Feb 23, 2024. + JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. + + ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. + SECTION 108(a)(3)). +*/ +/* +playback timings (ms): + exclusion.robots: 0.129 + exclusion.robots.policy: 0.119 + cdx.remote: 0.111 + esindex: 0.012 + LoadShardBlock: 121.768 (6) + PetaboxLoader3.datanode: 103.377 (7) + load_resource: 106.398 + PetaboxLoader3.resolve: 68.457 +*/ \ No newline at end of file diff --git a/public/wp-content/themes/twentysixteen/genericons/Genericons.eot b/public/wp-content/themes/twentysixteen/genericons/Genericons.eot new file mode 100644 index 0000000..7322565 Binary files /dev/null and b/public/wp-content/themes/twentysixteen/genericons/Genericons.eot differ diff --git a/public/wp-content/themes/twentysixteen/genericons/Genericons.svg b/public/wp-content/themes/twentysixteen/genericons/Genericons.svg new file mode 100644 index 0000000..4740685 --- /dev/null +++ b/public/wp-content/themes/twentysixteen/genericons/Genericons.svg @@ -0,0 +1,537 @@ + + + + + +Created by FontForge 20150618 at Fri Sep 18 10:24:13 2015 + By Joen Asmussen +Copyright (c) 2015, Joen Asmussen + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/wp-content/themes/twentysixteen/genericons/Genericons.ttf.html b/public/wp-content/themes/twentysixteen/genericons/Genericons.ttf.html new file mode 100644 index 0000000..0174438 Binary files /dev/null and b/public/wp-content/themes/twentysixteen/genericons/Genericons.ttf.html differ diff --git a/public/wp-content/themes/twentysixteen/genericons/genericons.css?ver=20201208.css b/public/wp-content/themes/twentysixteen/genericons/genericons.css?ver=20201208.css new file mode 100644 index 0000000..bd8e494 --- /dev/null +++ b/public/wp-content/themes/twentysixteen/genericons/genericons.css?ver=20201208.css @@ -0,0 +1,284 @@ +/** + + Genericons + +*/ + + +/* IE8 and below use EOT and allow cross-site embedding. + IE9 uses WOFF which is base64 encoded to allow cross-site embedding. + So unfortunately, IE9 will throw a console error, but it'll still work. + When the font is base64 encoded, cross-site embedding works in Firefox */ +@font-face { + font-family: "Genericons"; + src: url("/web/20221206204125im_/http://blog.boochtek.com/wp-content/themes/twentysixteen/genericons/Genericons.eot"); + src: url("/web/20221206204125im_/http://blog.boochtek.com/wp-content/themes/twentysixteen/genericons/Genericons.eot") format("embedded-opentype"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Genericons"; + src: url("data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAADakAA0AAAAAVqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAA2iAAAABoAAAAcdeu6KE9TLzIAAAGgAAAARQAAAGBkLHXFY21hcAAAAogAAACWAAABsqlys6FjdnQgAAADIAAAAAQAAAAEAEQFEWdhc3AAADaAAAAACAAAAAj//wADZ2x5ZgAABFQAAC7AAABIkKrsSc5oZWFkAAABMAAAAC8AAAA2C2BCV2hoZWEAAAFgAAAAHQAAACQQuAgGaG10eAAAAegAAACfAAABOFjwU3Jsb2NhAAADJAAAATAAAAEwy4vdrm1heHAAAAGAAAAAIAAAACAA6QEZbmFtZQAAMxQAAAE5AAACN1KGf59wb3N0AAA0UAAAAjAAAAXo9iKXv3jaY2BkYGAAYqUtWvLx/DZfGbg5GEDgkmLVWhj9/ycDAwcbWJyDgQlEAQABJgkgAHjaY2BkYOBgAIIdHAz/fwLZbAyMDKiAFQBE7gLWAAAAAAEAAACXAOgAEAAAAAAAAgAAAAEAAQAAAEAALgAAAAB42mNgYf/MOIGBlYGB1Zh1JgMDoxyEZr7OkMYkxMDAxMDKzAADjAIMCBCQ5prC0MCg8FWcA8TdwQFVg6REgYERAPvTCMQAAAB42i1PsRXCUAg8SAprl7FN4QZqb2WZGRjAIVLrHj4be4ews7OJHAd54cMBd+Af7JHmt3RPYAOHAYFweFhmYE4jlj+uVb8nshCzd/qVeNUCLysG8lgwrojfSW/pcTK6o7rWX82En6HJwIEv+wbi28IwpndxRu/JaJGStHRDq5EB+OKCNumZLlSVl2TnOFVtl9nR5t7woR0QzVT+D7cKLeIAeNpjYGBgZoBgGQZGBhBYA+QxgvksDBOAtAIQsoDoj5yfOD9JflL7zPGF84vkF80vll88v0R+yfxS9lX8/3+wCoZPDJ8EPil8ZvjC8EXgi8IXgy8OXwK+JHwp+Mrw////x/wsfHx8HHxMvJo8Rjw6PGo8CjxSPCI8fDwc3PVQ2/ECRjYGuDJGJiDBhK4A4pXhDABtHClYAAAARAURAAAALAAsACwALABaAIQAzADyAQABHAFGAZQBzgIIArIDTAOkA+AEEgTCBRYFYgW+BjAGwgbkByQHSAeCB+AI2Ao4CowLGgvQDBwM6g08DX4Nug4kDkYOYg6ADsoO7A8yD4gP8hAwEGYQpBDuEUgRshHUEfYSQBJeEnoSlhLEEtwTIBNYE6oT6hQaFC4UShSQFJ4UtBTyFSAVjBW4FegV+hYUFiwWQBZWFmQWchaIFuYXFhdUF4gXyhgEGCwYThh8GNYZEhlCGVgZZhl8GZIZoBnQGhIaShp8GtIa6Br+GzAbVBt+G8Ib/Bw6HGgciBy8HOwdHh1WHXAdmB3eHvYfIB8uHzofSB9WH6of4CA4IMghACFCIcQh4CIGIjoiSCJ8IpYiyCLmIxAjWiPwJCQkSHja1Xx5YFTVvf/53nUm++zJJJnMkpkJJJkss5GFMIQ9w04IS0BZRSJLMIIo1l4XFETQFkVFBKwVrbuWpRaXPOtalZaCPKu1D2yf28NX21qfQubk9z3nzoSAS//+Mbn3nnvuuWc/n+/n+z3fCxHIaEKEJfJMIhKVhJ4GUtP8jCqRz+ufVuQ/NT8jChgkT4ssWmbRz6gK9DU/Ayw+bPKY/B6TZ7TgpuVwN71Unnnm0dHS24QQRSACUYis8XyzST6xEAch4LF5ZJsnKkc9NsDDj2ETXgUikT4iaClNJEBSGoZIP74qa+l//YRfKB5EAEyj4g/ztWBZbslcIEjucqHATOpjkYBXsYo18DNYeOQI3UMvonuOHIHXj+/YcXyHSs7FLGQp+o7sYA8IFq+BpmqKhtk6SDEZinWVWfMsHlLfIkRCgjdPsLpAtMlRUu8CmzVP8HlDEInJmkC+wcbihT54cN/6cePW79Mv/f1E+MUT2zvCM68cOWt7Rwc2pk8TNQ3IWW0gEbuI3yxI7KW9HdtnjbxyZrhj+xPbWX0EYhjcf9h3Jg9gldjBfhLm1af1ERF7BTAEmoxngQDeU35mB/YPsDiFtU0gxChgX2tn8S6FP3zG38O+zMWEVkU1yaYQRCMxt13WblvTT9bcdgpaTsnahlcqUp9owt0Vr2zYc+oUHwN8S2FjwMYV62PNA5+pPhaFc0EP4JhuPr2la4eQCVCsNRvnLac3A9nRNShIBFZPXpciEmHjareZsEbRWNTEBhVvHDasmyniwP7HJ+4AhlsgbmOP7PUsWVA8DFmHuzoSa3avSXR09XZ0HaZfHa7raOARKjm8kWoLdwfuamwHbcqaNVOo1t54V2D3QtA2nsQL1TYePrwRtMTaWUWYhvI0gGlYz5FeldWtgPiwvfW8bpVgAk/cwxqtR/hwhHxeVq9YWNG6duzo0miCHtBgy55TlN/jbYIHFGwyi6IJ6NVO7RG0c7c7ugBDRITMuMlYqovNAFYeuNg4BWPRSBCDBRhsEaKRQJCl5mOvSfmxpqbY3GQSCmYvXjy7s6bVP2WcjI/P4iEUxG7ddWt0brKrC5/P+Yz2fTans2bNjWMvPTwOi8B2Vhtw5pEr+cpyCWabVVAkVQngpGDFtChYcIsQCIYgT1ADQUUNifmQB7g4HIrN6pIdiponhCAYkoJDMd7ucEkOlxK32q02qxIMlAewtuYWQVwLdsg6+fyNbcufpfRunw+CruicxZMm1JYsV4zGfIuUV9+8OH7VzTdfFV80IpSVVZBvMErLS2rHT140JxrJtYfGjRjrFIyl3liplFNkNDlFY6nTmwuKwx0fu6gZfL67aOrZ5W03Pn/SQNiZfrXlIfr62RfrVXeh9JvpoxY4FUt5/eRFm2bsvTy/YvzFdSDK5jq/F8DrrzMpglAxtSFekt2zZ/rmRZPr/WYl1JmVJxdEq6VcX3GhoGY7zaAUuoZ5pNwhrqF5WabyKXVZhW4l/MJZaHhoC28cdiIDKkJ4nxqIiZQittSTBJlKiL8+LogKUe3+mDleLrvAjLhidsRIPBDMAda9LsERkxwCsETlccHiVXx2S4sUD1SBWyIIewRxjzDgk8iBw54n/0w3db0rjt/1ViE9TY/nNXaeue+KFT+Cxz4uSNCP6Bp5+biD/9dsLw0qj8DEq51nG1+if695Cb68Zevjbs19yW+VvZO2LB9yLT1Er4JdsAEsP/85/ZxupEvw+PznPweLNhWq4MY2evS13r0roL03FCq+m/5W2Jx4iP5u/dsQm1SrddTDuw0Xd7lKw+05HqUYSuGfM+nhE/bxIXBCrGAf3Sc0ultay6/9qXZB5lggL5R1FyAeVyEef0Aa8EZR7Qi4kuRz++3helzyOL0wgJfhOL8YXsXtkgNnaIsQrrc7YvE8UGOqllwpVM/Vnvo9pdvoEdpfVTXzgZ+MuPJ5n99dV/vjhyfPTs6uvwVu+TCrcfGm5OQt4R+tsLY3rFJquycX25Yff/vwfT0jH5QDY+vEbavV3KI3b5QrxfqfXbS445E3s4dUtm1a3Dg8XpRILPfm6vUlKD9UjQQH0MGHKG3xDEcZEXbEAz4UIKUIiyg0zwMI+hHk5dCPKlv3yZOWX/TT2VWUpqrYAxUR4SxB6HwNpN6c5jj8Iyt28drRp2lfqmFHl4xPOLZjufLHWK6b4YPIBAMrI9IiYU+Ugejl5YrSbpiQT1+lvX/+s6N6/EXXtsW7nE51/pKKiNMofU2P9h0SJ0ANCJEFs8bHShVRpB+Z/NVeUTASRJ9M2yyIzB6yhKzi2GA3s0HxeXFFF5hjgDMXFKjHuZsNdgtYYvEWMRphQGBA6AjXOwLlPq+kqPXh+tgIiNkVVVHBIiKOxBz2c3F+HGpVjJmjEbENVsDEL7aN7Nn38idXH6T7v9i27Qv6pzNv0x+PFQO3XC8JX/+j+y/gmypIBXkW1VFoBYdslvMkVZjcCMZV9NN7b6H9R8YXF/lX+Lw2S561qhb8T13bbs23WjdOCVzm82GkrVLwycO/OvSeqmHu+w9e/cnL+3pGbvsCJvLSU3mn6YYlUul9fTUhWREeSo30SHv7dkOOklNXNzZcGJoT9Qp+gzu7JL/Qlt3QAUu6Ox9YJQsilHlFWei7SzDBbFXwuiErE6lWVN68M9XQBT3vH2FzXSC3wj9Rlm4ldWQ4G0W73q8hITOh1ZARh5FBLM5+Me7xh20+my/qi4ajYeE9IZAbGLPkmh3T1723++JF9797+do3WncKVqO9oMjucpWblz66ZMmjS0d2j48VSXS/uE9nVJIWDE/fcc2SMYGLd7+3bu37uy+ePPEeyFVzDdmqURIXP/rbRxeXx8Y0Fb3Nk2M9RZ13Kc8jJzFjXTkjCTJxx4YX4R/FPkZF2FQHFYWyxxz02FoUfCbYhPn0ILQ9KExbumxGvL0KqjrkAnpoWkfluKG52fSQJMGEbJvbUxNuLZ++eVkDEPG/bl40oW1h9aS62kmhszsF8/Ir/WF3cSz1n+L187eaSnzFxZbs+GWPr2ZcKT0/Gct0k+ZBKzC91Bg/saCYDoEPiYTVjhG8moIa9dgLbCrWOs672mbSVyVbeCiGHfSbG0ZPg6mto6ZPGyk1PbSpftowbwH9GgAMhixvg3fMyMwy1ZfkGSIW9X0sbpzS2DxpclPjlL4N8NqTB4sqg4XdHtpz4CAcrrQ5h5Re3E5nY2c+isJhGsqFqazGLkkf9kBQwJURDMQtbALEWKWsrD/ZGsFVEULemYdJkQSpeewvyOeJLNWt++MT2xZEqmdctePgksVPeicUeOffqZb+TMqzb71kxuxAc57j6iVrn1005obXfzT/0ZtXTQjOMKuqaBVUn33munj5xBV3/fIvBhJftGnvgfkbPnxx18rm+Qn6wbAN22MPXy08ZfQsj9x6+LLp4e3/0bD49l9B3cFLn76uLTSt+6a7p965yOYszJmSVWgy+u54rnvS7nu3rp9Vr+N4RvYtzvCJAiFPwGYGY3ELn8/AGiXqjbI77AgbEI8Fgmk0x6nD2CRS7TinOWxuYboywE5yBMiFXCIt5+/YliwZX7J12lW/u31a0+W73u5Zd3T3tVOGdC0zl8iCSZDlvNHjtN41Sx/oGjZ1x0XRdn9Odp1r3KjY3GiBwbjG4pAP0NO7BjMH+hn9iuU/dP1icEaTlx0G8c7Ox+9YnYhfdM3td7bdcmyoIc9iSGRZbaYpVy185uZpzctvm7n96zujndGaXVcObZ01+upk5TSLhfpnLNo8BRyw7sgAQRDIXmGBukDei4srn/PeAuS2BeXpq2yF2V9+SR/+MnVFOiDvZecv03d41eUlUW9Xc4gXbyQR+bkP0TuIkwWpYhx/FrPDjCITQxhlVjaAtSAHlaGfpu5bsco7bZ71qvaN1z0152hdxNo8YdiabkPBpsSYG1VioA/SFB1Oh0AZ3HYtlLWvuKLnboOV/p7+agr9+1NPzbu7FB5nbcjoT/mIDd9af0ZBIag27OnjZ+CanoKsl/J7Ac99nL0SgHeJplTgWvbqWgUqEw47kw9xEwoHnDaMeEZNvihvVFwaBb+gs0wF1c0TN93cM3/+ig0XXzSqNfJqVzIZqjapGm2iH9PIrqoqZ/ls+lHMbi8ra2i8boOwNuVLJObO2cKm52D8cJBqjsEX1J+4lQK7O1aANeKr0c05B9bNHkb2b8J5WQlepRSs9iaojw2GELGMvnSKqVBIzf/XvPk0/ez0ZjP932RUJtFkMqqlT+ejCCWn9Lf6TolkbCMqSKg7NY1JsVekA5l3knxp9QOooPSTbeSnZAe5h9xH7icPkoeZNodNsNUq7M+q1KHOoNQpqpWdFBsDFOxOJR9A8QahtgYCwdpANKB3byAYCfIVGIhiZAS7IFobi8bqIqzPo/VxftV/I6A2DrF6B9Ta62rtYbtj4GdjRy37szqsdXYwyXEjOPyyLQ4mv+qPB1UjBGV/VFVx1Pk/Af+E9BkvqVZThSnVCiLgdBZZrADn/RNgIDGKVuEFTC68AAIM5JHOCDArcH2cujJ19mNwpV59EO6kH34sjPv000+hUpA/ph8KjQ9K/5AlWi2oAkjsHVaowIpM54D5A63OzoFjLPt0TUX+HC+AL+GLEhyTZAFkEPCWHew1ngE7H8vOptXpFop6jqwMlgzfgCn07Rd3wmz68M4X9/5pVeoFiLx47+Rdu3ZhaPbOF+//06rz56oF5dwL5GM2V5GJFaCO5uaqVQsSYVTXBJQPDrsUV9I8AjEVgXUEMEzFFKiHWTgDUxiRRmStjdQhVQuUsyj+aoyBcAgUPUI4B8whIRjggocnY1Qcc2MP2T0TSiIqi0GO1w6XiLfsjfStAPXlOINQiAVZlojhEpYZDJjjMYyPK5KCcG+2SxI5yJgfI2T0Dkb8OAc8tpueWLlyidW075r14N4wIbn6rTtmlSdC2KNGEUb+/OVlD4Brodt/KX3/dnHo0I4tV6xrn7vgyWuT2V3tl9AvV14xvCXLsHPlqv9qanEkQxs3RTsstnBBVbS0am4gEDEYzEUFlfXFzki1udghK5VlFTWh8bmohxlt9jGBwFirTTYbi70V9spOj9cvCh0bW8Mza3Js5qmXrBtWPjJsKjaaHRsebp91+0y64TRsuqRp1o43eibdsNAZG9/TTQ899BD9dFxb7qzZUP2MyXwv/fSNdde9DyGdd+rNZLQzzUDvMqxdfRn945139E8Yn9dgm739re6xm9bWY1uzBEiuaLp1Q7j62jtTWaNuGtYz1FfiTV775ALhshdbJlmbWpZfds3637g80+d3fpgMV1uDwxcsnFlcWaZm5zkc44YMbfc4PBZByHGai9v8/haTXYFhlQKUTSh1eQSo9Pnag1aP0yIZi8rcc2pHXhYy5Yy5aHU00l5tsOfVDC+Pb2ieclU0P2flA303f/3WTTeuPXrvZVb3yq3T7qJPrN/QXer8rz27YOU99/7BJQk5t7xL/7x7H/3D+9f//8R1mT73Y3W4ej25BG9cuAjy5BAqSKY8A858HnIJsTiKJ5eI+ngspPiC3kAeJgOXWAZqSMLF0iK6RIe8Wy2aMGb26CZnXlnlitVXdl86K2E2I+waTFa3P1IaWdU+xmzxjB41rACGKdbEiNmTpo+oyxLKW6Z3zpsx0mKRCsKR5NgZ48aXFBeJJmeR0XhKdTQOKc0eP2rMww899bO7N8xzqkPEnKH1M+ffsO3QojmbZ8Qtcm6uqtD/EVS7w+3yuUqzzUKRKycXCr2VeeXV4jOpjwQ5W5It1aMuGzPx+s62Km++ASFJyS+sCCerqxdMm9hYlZP9htG9fNWD9786b/LlTW4hr6QoKz2GiEFXIAYNIddh79hVbgwNMqiRUCwy5iaivseUAtlmBWapCgz+YRqmD9rTgn3gORITJpusg2SINS3zB57bMnQgpo4Mw6QbDiy5auWUiZe//yukq6ZRdZ3r75y69cq2sYteeHB7z4wqekmT1ze8qX368g6Xu9xtKYjEOxdVDvWUOIpqIj5vkXPYsBkzu7ctXzGsIR7tnL1xXsswr6el9dLJ1aFCp8NWUlYV8/pikVlXHrxnVbfYuuzyJQdumNSYN3zFrmff62mfefnGqXeu76xL5lTN6Nn+4AuL5tPftl86e3hzRbDY6bAYjeZ8zCPkLXe7W0I2e3l5dai+FqmIMzhkQtuCS0a3BgMlVrPJ46ofMbTKbvN4orWFRagDJSdNrBkRCnH+jKyIKMzuGGESHXFX1wbwrFQiS+EcJSRUgomjOO94Zp1Gwe6ptyuaPVhkZ0cymmCsgSZGXjFu7lCtt27VwgSoiACeOWMLDAbYG01KpLiu3OAJ6mdM3ZWsqK0QtIvu/3qzbKr2lLTvnD5zrz+Q1Cn927BVDas93KIVJLVkBBmPesxmrGUMq6UPWwSJAY4VYC3TWqK9nKkzCrvzxzidV+0oE1iQWwesdgmsjhgzlyjEqzCzbsRi1e0/gBKO866MXoTpLCimHHILYgXrCtQSgn7R7mD3LpBezx/qyu949nBHvmto/rDbfkL/1hoKjRwZCrXC6HmtrfNaBU9lw5DqshmpLY+C75FH6AePPkY/eOQR8KU+rKiZWVo1pFGuxoEYUb1vWCjvilfoF/QE/eKVtQWllUXrZtTNKDn03/Nks9kGDYXT69qWL2+rmVIn0jOT/vxkycz62LyYaMh3VeZ3dORXuvKHgRJqxeJbW/VzKDS8rHZIQ3B4alnXgctWHOzqOnjiYJdwb03JxOHlDUJ7qCVUnUg9Fe8srq9b+uzGKVM2/mop6n/hkb4Z66oDC43whj07Rx4/pG75HcurJ4Wa6bU5CypCsXlsfSK/Znq6RnwkjuPBjDBM7RX5loUwHDw23VzOu81hU2VPRscKRh1x/aE0ze63e2sA5t03f4w2LwZqzega+bUtW16X7kMaoc7bPX/+7nmw/D6Mlo7Os/ttIS8tm3vPnGjnj0YfPeKpqfHAx5uef3HTZdU/Ptq5a+6cnZ1/qA0dZ/FEryPbP8B5nU/KM3ybb+Lo+jrbxkF+yPZyHBB3IamOOxRkxpn9GyTW7wWSXX76Hn3P35UMwHLZ1DC6wSSr3Kx+VN/iOcrs6Kl9LAF9H/z8hR1Sqc9XKhHdrvUCcqnWgT0WByFG0WTMiduMEHUIt8Ga1Od0O6wULBTDggVWpv4u5NPtqc9hDb0dLt+d+iL1xW61lb5FD0F56lnw0V/RtyAC4+kH9CFxL/0TTIDI2W/o28t66EvQ0rOMt10ghCpzsO0uMoa3XRUFNU9iKoQKeaBrOEwcMr6F65vtb8TNyLCYcqGzMKaZcMuiBxVo+dXZjdbIHFlWrEU1rjMGWaVX5g11Z1vL8suaK4RTXtlpSa2ylcr/dFpLyz6wFouCS5RcFvr3Yp+vGEZk2wtUsmgRpbTFarVV2MyCgTYU5IqyWlkh2xxVVSV09S/tZW5zn0GRcZ4U5jnzDLtyrT5vcbDYk2PhOMX2R9h+0GDtb9BmCPnezY/0bgfHOgFnLd9TYnsdqPw5PDaPGBZ6xd5+wjRETJ7i8jylIRPW+klmLmHJCmPHOdwqZYTMRqCESyFFKBHf7GKApmAwRdg+U5Ldk8weC5+HZcSftmtm2DQza+q7f4hNeCdZTKhsmcQ6cIH8XHf3c/Qs/ZCefX716ufhjrXv3NvZee87a3fRr3buhKw/wdBO+rRKVj+vJ2LJkefji8+fXd2588RnJ3Z27qRf0dcxuUToXPqfnTAV3tPnB9aJ8L1IE957GY7arSLrVQ/rTKmL72ZqTGs+tUfS+B4m/ezUnn7siD2nCBncrmxSTKp0W53JEw3b8LAw45c+rbj+mh4vNlQ+VlhYRqFzBg9NwM5ORvu4xiniOdXrRKYcSODZqWhn2RLStLOYjCVIsbNwIOCkhD2HXkx5fl1cZChpxLrUoqasioxHxS16iZ4mqK0PowJRAnU/VFUJy1JC4RJ1xRO8DMK0KYebmya/s8bSb0AwqFij4pxQETyNVRLcDtTnDn9X5QnJGajr4H3rYpwblaQJZdwohqdhm5g+MmFPOowc1Wb6oZ7OvHtuO5vVmF+/pwGU6GnYM37Q9DVzFsh3NQWi+qY5Xx8zYaZ6tXo1tseNCAcOQB2tRYA4qAFvPt+jUyFurx+BsAt/Fsrmpk6VNzUGvTnWYcLX+4WyA/6uwIFCs7lwf+rkgQCG/cIwnspfU5pnDIWnS88dSJ3c7/cfKGptLTwglGHwoL9rYG1ynC8gJdh3KqCUZjv15W7JjOyOIM9HBEMJhdhHNGq6+9n0+oFhkLVzdd/q9Ue+PLKenQAb/LfVmSe4dHY9eze8mX64fv2AfTpdFm/pBcWRdFGoXtgtUY9NNsHfvlVmauxAngZBE1dT07fKpd+cq5VhsG2cr7cSUsFtVza2FeOJMjj6gXqIOIw4UGzpCv+mOkomIb6S+jf14vKNQKWBKO+QXKxTKaJbNdv/Z9AWNEIMqyIagXe8EZi2FUNVI8aNjgLnXYifMpyl8hL6JfKeL5dSBc4shRwYCjl+WEu3Tnrl3Zcn0lvh8kmvrFjxypQUYWauU/SlhRxbZXyTypf09CyDM3BmWU9PXyVcAT2TZ0yfTG+lW/EKL+3RXzglRDk6n1dn5ofh46uOgDcIjDWyuiOtjDNLeByCFgcE46whqEtk8N7PmSM2KK7zTYkUeWC/ckoAWMBbcucvdm2/qH3FK0lY+8fQdWfJdRpt5M268//eSG3h1YC3u257eAVvWsuaEaf2rEDIgf2eoj2nhJN0L2vTlO3e6ZPhinfhQ54DvMoauDf1Fm/4V13LeRNfWrNgJQdjEBho6b4S2P/M7IX1MwIKo15IaLSX9mqQ4CdIyBfcayxNen+R29HPz8NA+nrFhNbX29eriQl+EhPqBfcaS8PmqJaWKxbEsyjzcLFVGqJ+ziLsKutBhlWIVHJ4wPgZPveTiQ44mo49ySgg0DCB4OxPA76mg4+eQuGJEYoOIOjiX2+KqyACXjMH5w1QirxhBzGy9WrBP5CLQSW0/BD1U/8hWi5M3L9f+jE9mPoUJtL9ggPaQHCkPmXYovMFDbs2i692BN4gMxqj1Ne0PqKJuGAUBpiUGahTvdBLE+f4MeMLRu6TZAT8M3kYi0jhT8TfGQxzF5pedmJVJRLvv16lF98zkDzGdIwCW90OHIoaQfXjfMQ+6u3TaELUUo8vEGak9moLEgs0mIThBQqW3qdBL7acPetbwJ/lskdp/oS5syE2Ztx8VOQ5jPYgDCVS/E1WFegdjDc5uLY5g+a+Gp6IUO4z1aMYcwLeZEGgCnxmphyhmAWi7zm09ZMjdPfvj8I2mAYlr67qJ/Me/Jx+TA880b23G//kjLvE72HREZGsepX+lT5JLz/6BCSh6PMH5/VpPB2X7f3fADEo6ovYG07uo+JCecJ1UlyiLcgsBpZmMXgs6luVeZErZnxzunVZs8PhE76u7L68u5L+H193f4zQj8LC3LHa/LgvMbNrmPTO2AkTxp45ylcVRNmeAQ5MZp/BhtgQ1nkNQwXUXeJc3+RIhqCG6Oth0GB3sMYH1ZAgcBqleJnHFv1tkv7mpVkPbm0E1AoC0S2TmIMOHqi+JmH4S9d/MofFg2/G4i95YyWcSo8dD7U3AWoT/tjwU0IZ28h47PiSOSwCyutLaS3vPd3fivsxVWa8mPLAyzg9Liu7m7sz+bwDTkt8rXGazJ2XOIJrLLRmytRuXDcauzLXpZR2NcP2qxk2MD8lQZuypntqmmy9TJvZnUA2snUBP1HY3Mgjhbp/HIKnyrA+GjGjClHAii+wi+VccsyZSpfT5VPn7IR9Nz733I2Ys0qYNFl7DB/AXVOPrd0FWSnnc2B4jjlTMTxbwPBMPsmWEJIJH8QdMucl9KR2Uj65IEVgr9aLY4Vz1EAGuBQpwsFi48WuBvI10Q82k3GZ4pHionAQZ7CQIZhHEFd1HrMLO0w4iKwJzALi8JjKcIJxDwMTTn34y18E7ZOa0f4/PnTz6UcXrZc3DVs69i8pzfLO+KlLnljF4pRSvP8k1L1xzNP0b1X0jH3zqyDeugvsdPKlrz48Dt+3vDP215euPbKtFBR8SFNMJxGxrZLGW8OWpcb87tL1ZPjDOoG1j89EfzrFWVRP+vC9PsKd3RjSzBASBtZnKtczy9gq5/wgfQGHlN7vM6fXizCM/gu2a9QCa6UH04HuvlE4Mdgw/H33mjW718j30zLEJyLsSZ3Sry0L2VOcPvTwGpbkPG6icj7L8IW7kg1emTL3HUNVCa+QPLceEYnTsSJ3IBu8GAnLisuUdN4ZphzXmTJJ4475gqs/7f2pM2Vd/Mhc8Hi4EEK1Ecmzz8TSCPu48Bj8B2nnRuZHmRFDNKGrA/ycwMqx5zgI/A3QX6T6ZZ9OjCVOm5lE0nM9yzVK5oTKCB0j4kRlumgJ12d1cRiJNUHajsVtTNw+OWizT1UPb2xdVxV67vI9pwolwvWyHWWejYfD1Us3nNrT0srXpqaCKqf9Ye1Wxr+DbGEEA5ERbCdNRFquHEwmP207mqQN9CS8Bm1tnyaPt83e20/2yruSx/ARjKcN4GaPjuNdW2rHXiAMkIHJLpnRKPVc/4t6RWS9Qtym+Af5f+UnuKwRsPCoByQCn1PLLJjFXFTpL+THqYVaOmCWBrO4HRIX2B8UTX8H1zySWyS1EplFf8G8UGHWLGqRH++gv8B3O+BzrssnFFYPxuiYgASEiFRvCllNr8xksYDUJsHTMSxJsHRYFyMm41YCIYE/jQlsDKZ6B3wJRKwe88bEGSxyd9o+Pg8BVyhWTX+Gc5st0syzNE+QNe6STIwiq7zGSBmbAWeJoDsecx5fwG5kTfm2/ucjQZzZNShz4lwTJBl9jx3xsM03+D48SB/8vnthgEylMqE+7cLAgAN0xgP6e0K8awRuB+G2DFbnb+1iZ5CF4ZisG2T4WbeNMEMJs5718TiJObNo6dUu4qM0jvD8GX4FLsg/zASuzRcdVI4YZYownCtKYxlpmQI5K2NWwEyZqOExxfhcwQeYituv2xAydnCGM8U6FjN5Lqev4LEKCiOAIRBEfIc3iF/6cJBv+vQn/eQnn96kcODglnD9mnrzbvqvX5bSf0Ju6S8hm9FEoq97Ja3FMXxOAwBDq8Eg4IIBFJCwesz1FnDe8NZi43SHX0U5vLGqfVypDgoCVk3HLmBmGyZH8OJ2bzzsqHSlMeIc9pQPYI9ej+8rPe1JSDJ10If1/JI5HOnQ+R1lCtxfn/EqI7fgmdjWlkfl8hqBGDECFy3zLmf6JzNHpN6bKwToXIGNEMV1xy1yKMD38Qfn2bDymZgo5c4cePJFue86MKjFNP2MZbNhuUpNsdXI8gaUm/q6TY+5iY84kxBNyGrTs5nVLRCJc41F4apFIjN1+4hYX1/fd4TZo9hU0vT5fBZLi/80zjRNAdFyj7pAXUCq+M6K6ldUixpkRDFoCQTlINMf48G4HIuLcQeictwh2h1+h2rHseaT216vLmikv6tptm95Y4Sz5Y0ttqZa+rvGTwyGTxqhrrbJtuWNkdaRb9xqb6qFOhZNN3H4FU7fam+uOZdSzyA3O4E5NNfoST/RM771dcy4jGM3ucDGYEV9/rwvH4Ab+VWI+fnOaRyUC7+BkOo3n96yaYNweHwf4aHUmPHf+iAidWTL6c3jU2M2bGJX4fCGb/GH4nNypTyjVyCgstXPlrusc4eUfmEsCGGYsEkj4ezRY/XF/SaTwWx1n5srOo8y6SyRxWZEvUx0qGbceoBz8ZTsyxH965GBbxIyOK+7D4n48AwrnmTwftD+QyYtkiELm576dyB6iSkuIAa+nyCDvp/A0tLfT4jAHbwN34u5ZBDm6kbwNNalQRc7x4AAeEZfsXj+OgO6vKoixyOWv4LaFcNcjqnG84rxpH+DihPS4CoMFAm82rj0M0XzL1Gw/0UtUzy+hO1mrR+oxoXzznLhvJMym3TI1zy2MDK3C+edsExH+720V9v7rQlXz4vpSzJooWk5dl55ju/+wodx1m995ZMazFsvKOjskfP0yPPKCH93GfrONa4qB9+uZkDLfqUQjnIPqO8pH170t7ffsf/n825aUlHkLCyKjC52vmUyj5n+fXUSGhqndSdGXrR/XEFBia+k2Du0umpkg7fUaquOpH3hdZ1Xn9Xsp+K8YYYKjrknqRuHzQ0nL0jLEhpZ2hSOvESYwZ6lZcyHupk9I2MHYUzHTOz4RhgVg7AFj6DPb0HNLlzMggqjGimWeQe00/85UamlPuvgtkitYwTeybwu3I7JE6bDvO7/xPrkKtvYTgbTQFsEexnEW8CF0horv35CU/DGZ1+YcP/9E1741caK5gk4ZZeO+c1r97YMHXP33WOGttz7+ktj2Jwgl8BJdafixhWsfw3F7F8iqBbRwQzaQeGyE/Qo1Jw4Kh09cfToCag52/U1kK/lhm3IoRu2QQO8to2+Rl/bBq/RshaJtDCdjOunaTtQEdv9MQpRFLSoxX3LgTjKtTREubBJNxIpiCqsnX0oqges7lEm33UTrcxhhFnz8IRU9lwKbtMfMPp+ux6lP1wP2w+Xn/p3JWvkO8os+4EyLSj+g+oPldoHL8+lOw50/lDJOH1e7mSJGIqm56iMcgzLNRkF5rRgCqIIY/Y0k8CtngyARYJyaEfbc0v6OR7LCWYdpb18CrMPyujxHW0Tqabfp/0ldFzP4z7Vg3OVL8iLfMf752wPIuuTjCzycgdl0Weq5w4WHD0kPsnHrk4mV48dt6Il3ODzNYRbVozjMcB7SsaVxzRSdogDoUEYx/lRNrPSQBrEeYnMv9kT5Fv1wC0jDLgljS2shmHdKdLtDxcxNS/FxaPE51EfSW6Nr1lTPvfiem0wd+K2hguHlDkEurFzZE+Uf1qncEW4j583nwb76c1slxR5h3TeGGq6J6rG6SbTNwQiz8I2FBAn99f1cJRUVBt3QfF5mCmOQWglFOlBH8qkZV+uXr1w6sqFf/0NnQbk+iVz6uouXbt96YK3FG3smHuW3ZinFt20+r6nhV8NH9daWkpb6PFJU28jaTs6kTP7wz4xrHriYYsv7pFna19oFTRRwS6oXnKFikvOtM1b49wim2EQ6+eMYwmYgswRk7MLOJCWxzhxe/s5Vko6Xel7U0j0phaAm00QI/ezZv3KeIOR5HB/ZxuOIMp+i8ljYR8asNk2BEC3DKt+I6BKr+nKDWjf8DHTzS2gm5i1bzROhPFeThNjiqVnDC9shEHjLErjagYztmnny0kz+Y/zZZgjqKgjuLtlMF4j5EONMEJ1jIAyCNRAvhQcAY54cIQQCKoO/MsXWSK8RVkXR3jmCeP5QhnGYaAM8iGuloEazzcEK/HGEccMJYdaIyvMXdNRI48QkDiPEPBtScWkIuboyMdZd6GIzBPFLNnkEsjLkGhT8n1FhcMiFUEAWXbkWnL9geJRzsJch5xX6nCGC8XcGkOhrSJ/Yo9k9Ug2Q/OkZqUgJ2R3j3FdtuidJwO1bl+NSynJrk2Wx3ODxV6Lx2MszbYmY0PlvOxQgbMsz+fMcjsNhaFgnVLamD8kWIUKowEMcpYMTtc1726SsrJHubPUPIMh35rbHBTyLaPrvEaDx1BTWyY4Suoryk2CRxr6LcH9L0mxIMPum/zHp7LCRQaLTSyNueOq2ZdndfogS/VnNcdkVbD7so0VTtHuNNqz1ycFk5wlGLN8pc0em9VkMIH/ZsgxGBTVLDrkItvQfHOJN+AwmbPiVos9x1SgWixyvsliLXQ2O2srKt2uSqfRPKW2oNWUZcpxlIcWz/gJ7X+mPOeWEa3DSgqiLXK2Uc01Fxepdq9FrjMWZEuWxpGjyzplh8mpcBm6V3SrC6SMDfJbPH6Az/t+fcMNv75BFAdfpJM38Ougv7SfJLO79DJUxzlvIF9rYq84YK/BGwNbKyRqArEXUb8vwd6REnwvC+ORa/BYA+lLcDtOIr3PJXD+wqL1PAfbACpILRmmf6+sey4hJ/Po3y2nv5YxIWOLDYd0VHl6wUtpYodI08i/Ru4njWOZLtwYuPqmrh083KfvRQrJtMPI2LXeB5jc6NIkn3fdGIZ8oY5WB7WP29H1gHftWIyw87QHMoRZGdAtzv/2PS1LMps7me+4gejSpI8wBV5EAU55jMhAgmlOeFCSCQHnYXqY41ucY4BGcvX9EKOIOjEEWyS+Y+rzBiEaDCj5oDBfLodubiyDcyYaAp9igf/0+8EP3MtP/G0M2xGjBxPOTv9Ef5c/X9Dy/RjKdya0p6KBQNSvatSBtDPX3xWAclG2jZu+8QyNTkx2xaBNSzjzMbH+VheGOp2J1L/wJX+UkMHfEo4mE0k7mUeW8D2jtE9gC8SZU6DHNBDDfGzZ8A6KiHLlf2C0mdUHrxlQH/D8ueCqDgx1Mpoe9rGN/Sjx0kG2m5MOMiealD4N+tJq2vmX+fq484nwAJKqD9L3Y9Z5wZeMPpCeJ3j7wJ5TkJk2OJPoB6f2pMXKmeQgZTiZmTsC9skpNaH08v00ou/Lh42CiGzXwbZHM2tWfsS3plXMFmh3v84k6fH/Hsc9A/Cnb0TJPdEWoe+kwGcPqoOzerYxkxi7F36W3sETYBWuqZ/imvLwvRYH9w6Iu8BhYh7XgzrZFrb5TC2Q6WaZ3rGMPkCX0AeW3TH2lR5NS/edpvW8Qn+kd9OROY/+9s1H5rRdYoF/aQ+c64UHNJptWSqm0o0W0nOCkMk4H3SLVyX75tdcCqytwyESZFt85UFlIMIcDwR9ujUsEg+YeC3xoUtwtwjML47dFah2m98bCOreoI48QeWbBG/neucuCkQC18+lX+28h/5rzg14s3iOJ+9t9rS39D68XfrY5yB9/thSDO4qSWk7U8Pn/mNT5+M/aarY8mu+qTCybRnt38rzS5x49MpbNl/52HH9bivAsgmtmGTqgiMg6HHXY1aY5fX6He0/0tmh/WLzwpXhzsTcWyZnbF3aoL1swZNGC1nTTXps3TOeInHGwMaQMgSAAQ7AuI09bPJWAclCLcHqUO3EIb9+371H6eX0SfrXV1cJpOv5S6D+sBgOU7LqVSiBabDt6Ocnnn+a/m06r8OrOBca+f8FUcr9zjhX5CTaGg8rAjOvBoRg2AXumDR1z5o1UyJzws/2Wr98up88/aW11/EOFB8XtTVTBDJlTXhOhJKpBYfoF0PoF1AwBAoObT50KO3TLGJLB++pySS9p3buO2pHxoLDDZ+mwWE13SeDzpxAZc6MOn1XPKTfy+gJvL+zM9+Z6T/mLsDwltnSGbHWQ6y/+TduhNfNyHbRQPTIoh//PCIKMe654JHIOroVqtahHh25Eqro1nXHhMdT77yTOpE68U7qHeFx+WN6zx/onvffh4V/EFENodekboRb6DrhGrgx8917poyMP4SnGFCFH5TJsWOo7g96Mb0ZN7h++YPfFnklL8zjWKaK386MVrD6wbK07x7X1ezI8CuZ/cmIs4vtZnOc9nBvczbv1EAQYZk9hfq43cFs1gof036udnWxweCBueOHzLphj77r20f0O8q4MQcyLpaBpP/TkKZrF3Xq8ZSH4cLv9arJBLLoO7029Z3hgId9i8x2j+3hWJhv3NnjulJSnv5M2Wp31PNHkqPebhl4xp+EM0/s4njohol/27r1b3Q/vZ3uZyGxy+LKN+bn/Z3+NXb1xNEmk6nI6cz95SU//uKiXK2kPLiJPvPIuFunjA6HyhSn0vPLn0OgK8epuWrCd9Dr3+l7JBEO5Lvlx359GGZfXaRqg7OGiby4s8vykRcX5qlbTWaTIbvYbHPlOpsacj6qcTVYJ8/GEk3NJZGs3GDbqFxwRvxh57xZYduYQDg3MCWZc15fidybtIjNdh//TwL4ZrzoyzARWxxn7y6hZFffxcpwWk3v/+yvlChLzpyFiz+Fx+THaDUcYwccP/s8HcUIiPR6apQ45+yOY8c4DqVtSen95cHaJhPPusJznmcmV3XYyuQx/Pz/AAfdhq542o2QsWrDMBCGfyVOSjOUDn4AdSlJiY1sMCTZ0hQHQqcM6RyMahsSKVj2EChd+wgd+wZ9s7xDz4pKl0IrkO7T3a+73wZwhU8wnNcNHhwzDPDiuIMLvDvu4hYnxx4G7M5xD9fsyXGf8q+kZN4l3e7tq5YZfDw77tDcN8ddPOLDsQef+Y574Cxx3Kd8gQU0DjiiQokcBWpwDJFhRDGGQIQEY+IV6SQU0RwGezR0GpvBQh+OVZkXNR9mIx6LKBnzlZaKz82+MUaSZGmV0k7JqJOit1hKJasy04p4TcWcmu6wJRHWMm92W4LUimsbK1JIayskYxwz2r81PlciTBBgSvv7M5BqVae6yiWPQ8Fn/McAXaJJMA1a8/9wu7FFQ2Vtf4mwE0IbW2fYyMqUWnEholAIwf/u+QXtVlqxAAAAeNpt0meTFVUUheH7DhkJEgQJgpIFhdvn7NM9gxKGCZKzKGZyUHJGySAgSq7i5wrFfYdPdFXX+tRP9V61Wl2tt8//rdbh1vueV29eWl2tYXQxjOGMYCSjGM0YxvIB4xjPBCbyIZOYzBSm8hHTmM7HzGAms5jNJ8xhLp/yGfOYzwIWsojFLOFzlrKML/iS5aygTUUiExRqGrrpYSVf8TWrWM0a1tLLOvroZ4BBvmE9G9jIJjazha1sYzs72MkudvMte/iO79nLD/zIT/zML/zKb+xjPwc4yCEOc4SjHOM4v/MHJzjJKU5zhrOc4zwXuMglLnOFq/zJX1zjOje4yS1uc4e73ONv7vOAh/zDI/7lPx7zhKc84zkveDnqwsljg1W7bVZmMrMZZjFrszG7zZ63mfSSXtJLekkv6SW9pJf00pBX6VV6lV6lV+lVepVepVfpVXpJL+klvaSX9JJe6njZu7J3Ze/K3pW9K3tXbg9915id/wid0Amd0Amd0Amd0Il3TueesJ+wn7CfsJ+wn7CfsJ+wn7CfsJ+wn7CfsJ+wn7CfsJ+wn0h6SS/pZb2sl/WyXtbLelkv62W9rBd6oRd6oRd6oRd6oRd6oVf0il7RK3pFr+gVvaJX9IperVfr1Xq1Xq1X69V6tV6tV+s1eo1eo9foNXqNXtPxijsr7qy4s+LOijsr7qy0h75rzG6zx+w115l9Zr85YA520l0Wd1ncZXGXxV0Wd1ncZama1x+EcTsAAAAB//8AAnjaY2BgYGQAgosrjpwF0ZcUq9bCaABTzgdAAAA=") format("woff"), + url("/web/20221206204125im_/http://blog.boochtek.com/wp-content/themes/twentysixteen/genericons/Genericons.ttf") format("truetype"), + url("/web/20221206204125im_/http://blog.boochtek.com/wp-content/themes/twentysixteen/genericons/Genericons.svg#Genericons") format("svg"); + font-weight: normal; + font-style: normal; +} + +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: "Genericons"; + src: url("/web/20221206204125im_/http://blog.boochtek.com/wp-content/themes/twentysixteen/genericons/Genericons.svg#Genericons") format("svg"); + } +} + + +/** + * All Genericons + */ + +.genericon { + font-size: 16px; + vertical-align: top; + text-align: center; + -moz-transition: color .1s ease-in 0; + -webkit-transition: color .1s ease-in 0; + display: inline-block; + font-family: "Genericons"; + font-style: normal; + font-weight: normal; + font-variant: normal; + line-height: 1; + text-decoration: inherit; + text-transform: none; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + speak: never; +} + + +/** + * Helper classes + */ + +.genericon-rotate-90 { + -webkit-transform: rotate(90deg); + -moz-transform: rotate(90deg); + -ms-transform: rotate(90deg); + -o-transform: rotate(90deg); + transform: rotate(90deg); + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); +} + +.genericon-rotate-180 { + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -ms-transform: rotate(180deg); + -o-transform: rotate(180deg); + transform: rotate(180deg); + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); +} + +.genericon-rotate-270 { + -webkit-transform: rotate(270deg); + -moz-transform: rotate(270deg); + -ms-transform: rotate(270deg); + -o-transform: rotate(270deg); + transform: rotate(270deg); + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); +} + +.genericon-flip-horizontal { + -webkit-transform: scale(-1, 1); + -moz-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + -o-transform: scale(-1, 1); + transform: scale(-1, 1); +} + +.genericon-flip-vertical { + -webkit-transform: scale(1, -1); + -moz-transform: scale(1, -1); + -ms-transform: scale(1, -1); + -o-transform: scale(1, -1); + transform: scale(1, -1); +} + + +/** + * Individual icons + */ + +.genericon-404:before { content: "\f423"; } +.genericon-activity:before { content: "\f508"; } +.genericon-anchor:before { content: "\f509"; } +.genericon-aside:before { content: "\f101"; } +.genericon-attachment:before { content: "\f416"; } +.genericon-audio:before { content: "\f109"; } +.genericon-bold:before { content: "\f471"; } +.genericon-book:before { content: "\f444"; } +.genericon-bug:before { content: "\f50a"; } +.genericon-cart:before { content: "\f447"; } +.genericon-category:before { content: "\f301"; } +.genericon-chat:before { content: "\f108"; } +.genericon-checkmark:before { content: "\f418"; } +.genericon-close:before { content: "\f405"; } +.genericon-close-alt:before { content: "\f406"; } +.genericon-cloud:before { content: "\f426"; } +.genericon-cloud-download:before { content: "\f440"; } +.genericon-cloud-upload:before { content: "\f441"; } +.genericon-code:before { content: "\f462"; } +.genericon-codepen:before { content: "\f216"; } +.genericon-cog:before { content: "\f445"; } +.genericon-collapse:before { content: "\f432"; } +.genericon-comment:before { content: "\f300"; } +.genericon-day:before { content: "\f305"; } +.genericon-digg:before { content: "\f221"; } +.genericon-document:before { content: "\f443"; } +.genericon-dot:before { content: "\f428"; } +.genericon-downarrow:before { content: "\f502"; } +.genericon-download:before { content: "\f50b"; } +.genericon-draggable:before { content: "\f436"; } +.genericon-dribbble:before { content: "\f201"; } +.genericon-dropbox:before { content: "\f225"; } +.genericon-dropdown:before { content: "\f433"; } +.genericon-dropdown-left:before { content: "\f434"; } +.genericon-edit:before { content: "\f411"; } +.genericon-ellipsis:before { content: "\f476"; } +.genericon-expand:before { content: "\f431"; } +.genericon-external:before { content: "\f442"; } +.genericon-facebook:before { content: "\f203"; } +.genericon-facebook-alt:before { content: "\f204"; } +.genericon-fastforward:before { content: "\f458"; } +.genericon-feed:before { content: "\f413"; } +.genericon-flag:before { content: "\f468"; } +.genericon-flickr:before { content: "\f211"; } +.genericon-foursquare:before { content: "\f226"; } +.genericon-fullscreen:before { content: "\f474"; } +.genericon-gallery:before { content: "\f103"; } +.genericon-github:before { content: "\f200"; } +.genericon-googleplus:before { content: "\f206"; } +.genericon-googleplus-alt:before { content: "\f218"; } +.genericon-handset:before { content: "\f50c"; } +.genericon-heart:before { content: "\f461"; } +.genericon-help:before { content: "\f457"; } +.genericon-hide:before { content: "\f404"; } +.genericon-hierarchy:before { content: "\f505"; } +.genericon-home:before { content: "\f409"; } +.genericon-image:before { content: "\f102"; } +.genericon-info:before { content: "\f455"; } +.genericon-instagram:before { content: "\f215"; } +.genericon-italic:before { content: "\f472"; } +.genericon-key:before { content: "\f427"; } +.genericon-leftarrow:before { content: "\f503"; } +.genericon-link:before { content: "\f107"; } +.genericon-linkedin:before { content: "\f207"; } +.genericon-linkedin-alt:before { content: "\f208"; } +.genericon-location:before { content: "\f417"; } +.genericon-lock:before { content: "\f470"; } +.genericon-mail:before { content: "\f410"; } +.genericon-maximize:before { content: "\f422"; } +.genericon-menu:before { content: "\f419"; } +.genericon-microphone:before { content: "\f50d"; } +.genericon-minimize:before { content: "\f421"; } +.genericon-minus:before { content: "\f50e"; } +.genericon-month:before { content: "\f307"; } +.genericon-move:before { content: "\f50f"; } +.genericon-next:before { content: "\f429"; } +.genericon-notice:before { content: "\f456"; } +.genericon-paintbrush:before { content: "\f506"; } +.genericon-path:before { content: "\f219"; } +.genericon-pause:before { content: "\f448"; } +.genericon-phone:before { content: "\f437"; } +.genericon-picture:before { content: "\f473"; } +.genericon-pinned:before { content: "\f308"; } +.genericon-pinterest:before { content: "\f209"; } +.genericon-pinterest-alt:before { content: "\f210"; } +.genericon-play:before { content: "\f452"; } +.genericon-plugin:before { content: "\f439"; } +.genericon-plus:before { content: "\f510"; } +.genericon-pocket:before { content: "\f224"; } +.genericon-polldaddy:before { content: "\f217"; } +.genericon-portfolio:before { content: "\f460"; } +.genericon-previous:before { content: "\f430"; } +.genericon-print:before { content: "\f469"; } +.genericon-quote:before { content: "\f106"; } +.genericon-rating-empty:before { content: "\f511"; } +.genericon-rating-full:before { content: "\f512"; } +.genericon-rating-half:before { content: "\f513"; } +.genericon-reddit:before { content: "\f222"; } +.genericon-refresh:before { content: "\f420"; } +.genericon-reply:before { content: "\f412"; } +.genericon-reply-alt:before { content: "\f466"; } +.genericon-reply-single:before { content: "\f467"; } +.genericon-rewind:before { content: "\f459"; } +.genericon-rightarrow:before { content: "\f501"; } +.genericon-search:before { content: "\f400"; } +.genericon-send-to-phone:before { content: "\f438"; } +.genericon-send-to-tablet:before { content: "\f454"; } +.genericon-share:before { content: "\f415"; } +.genericon-show:before { content: "\f403"; } +.genericon-shuffle:before { content: "\f514"; } +.genericon-sitemap:before { content: "\f507"; } +.genericon-skip-ahead:before { content: "\f451"; } +.genericon-skip-back:before { content: "\f450"; } +.genericon-skype:before { content: "\f220"; } +.genericon-spam:before { content: "\f424"; } +.genericon-spotify:before { content: "\f515"; } +.genericon-standard:before { content: "\f100"; } +.genericon-star:before { content: "\f408"; } +.genericon-status:before { content: "\f105"; } +.genericon-stop:before { content: "\f449"; } +.genericon-stumbleupon:before { content: "\f223"; } +.genericon-subscribe:before { content: "\f463"; } +.genericon-subscribed:before { content: "\f465"; } +.genericon-summary:before { content: "\f425"; } +.genericon-tablet:before { content: "\f453"; } +.genericon-tag:before { content: "\f302"; } +.genericon-time:before { content: "\f303"; } +.genericon-top:before { content: "\f435"; } +.genericon-trash:before { content: "\f407"; } +.genericon-tumblr:before { content: "\f214"; } +.genericon-twitch:before { content: "\f516"; } +.genericon-twitter:before { content: "\f202"; } +.genericon-unapprove:before { content: "\f446"; } +.genericon-unsubscribe:before { content: "\f464"; } +.genericon-unzoom:before { content: "\f401"; } +.genericon-uparrow:before { content: "\f500"; } +.genericon-user:before { content: "\f304"; } +.genericon-video:before { content: "\f104"; } +.genericon-videocamera:before { content: "\f517"; } +.genericon-vimeo:before { content: "\f212"; } +.genericon-warning:before { content: "\f414"; } +.genericon-website:before { content: "\f475"; } +.genericon-week:before { content: "\f306"; } +.genericon-wordpress:before { content: "\f205"; } +.genericon-xpost:before { content: "\f504"; } +.genericon-youtube:before { content: "\f213"; } +.genericon-zoom:before { content: "\f402"; } + + + + + +/* + FILE ARCHIVED ON 20:41:25 Dec 06, 2022 AND RETRIEVED FROM THE + INTERNET ARCHIVE ON 08:38:14 Feb 23, 2024. + JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. + + ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. + SECTION 108(a)(3)). +*/ +/* +playback timings (ms): + exclusion.robots: 0.173 + exclusion.robots.policy: 0.161 + cdx.remote: 0.119 + esindex: 0.013 + LoadShardBlock: 99.725 (6) + PetaboxLoader3.datanode: 129.994 (8) + load_resource: 5183.277 + PetaboxLoader3.resolve: 5122.176 + loaddict: 37.168 +*/ \ No newline at end of file diff --git a/public/wp-content/themes/twentysixteen/genericons/genericons.css?ver=3.4.1.css b/public/wp-content/themes/twentysixteen/genericons/genericons.css?ver=3.4.1.css new file mode 100644 index 0000000..63242f7 --- /dev/null +++ b/public/wp-content/themes/twentysixteen/genericons/genericons.css?ver=3.4.1.css @@ -0,0 +1,283 @@ +/** + + Genericons + +*/ + + +/* IE8 and below use EOT and allow cross-site embedding. + IE9 uses WOFF which is base64 encoded to allow cross-site embedding. + So unfortunately, IE9 will throw a console error, but it'll still work. + When the font is base64 encoded, cross-site embedding works in Firefox */ +@font-face { + font-family: "Genericons"; + src: url("/web/20220430225703im_/http://blog.boochtek.com/wp-content/themes/twentysixteen/genericons/Genericons.eot"); + src: url("/web/20220430225703im_/http://blog.boochtek.com/wp-content/themes/twentysixteen/genericons/Genericons.eot") format("embedded-opentype"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Genericons"; + src: url("data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAADakAA0AAAAAVqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAA2iAAAABoAAAAcdeu6KE9TLzIAAAGgAAAARQAAAGBkLHXFY21hcAAAAogAAACWAAABsqlys6FjdnQgAAADIAAAAAQAAAAEAEQFEWdhc3AAADaAAAAACAAAAAj//wADZ2x5ZgAABFQAAC7AAABIkKrsSc5oZWFkAAABMAAAAC8AAAA2C2BCV2hoZWEAAAFgAAAAHQAAACQQuAgGaG10eAAAAegAAACfAAABOFjwU3Jsb2NhAAADJAAAATAAAAEwy4vdrm1heHAAAAGAAAAAIAAAACAA6QEZbmFtZQAAMxQAAAE5AAACN1KGf59wb3N0AAA0UAAAAjAAAAXo9iKXv3jaY2BkYGAAYqUtWvLx/DZfGbg5GEDgkmLVWhj9/ycDAwcbWJyDgQlEAQABJgkgAHjaY2BkYOBgAIIdHAz/fwLZbAyMDKiAFQBE7gLWAAAAAAEAAACXAOgAEAAAAAAAAgAAAAEAAQAAAEAALgAAAAB42mNgYf/MOIGBlYGB1Zh1JgMDoxyEZr7OkMYkxMDAxMDKzAADjAIMCBCQ5prC0MCg8FWcA8TdwQFVg6REgYERAPvTCMQAAAB42i1PsRXCUAg8SAprl7FN4QZqb2WZGRjAIVLrHj4be4ews7OJHAd54cMBd+Af7JHmt3RPYAOHAYFweFhmYE4jlj+uVb8nshCzd/qVeNUCLysG8lgwrojfSW/pcTK6o7rWX82En6HJwIEv+wbi28IwpndxRu/JaJGStHRDq5EB+OKCNumZLlSVl2TnOFVtl9nR5t7woR0QzVT+D7cKLeIAeNpjYGBgZoBgGQZGBhBYA+QxgvksDBOAtAIQsoDoj5yfOD9JflL7zPGF84vkF80vll88v0R+yfxS9lX8/3+wCoZPDJ8EPil8ZvjC8EXgi8IXgy8OXwK+JHwp+Mrw////x/wsfHx8HHxMvJo8Rjw6PGo8CjxSPCI8fDwc3PVQ2/ECRjYGuDJGJiDBhK4A4pXhDABtHClYAAAARAURAAAALAAsACwALABaAIQAzADyAQABHAFGAZQBzgIIArIDTAOkA+AEEgTCBRYFYgW+BjAGwgbkByQHSAeCB+AI2Ao4CowLGgvQDBwM6g08DX4Nug4kDkYOYg6ADsoO7A8yD4gP8hAwEGYQpBDuEUgRshHUEfYSQBJeEnoSlhLEEtwTIBNYE6oT6hQaFC4UShSQFJ4UtBTyFSAVjBW4FegV+hYUFiwWQBZWFmQWchaIFuYXFhdUF4gXyhgEGCwYThh8GNYZEhlCGVgZZhl8GZIZoBnQGhIaShp8GtIa6Br+GzAbVBt+G8Ib/Bw6HGgciBy8HOwdHh1WHXAdmB3eHvYfIB8uHzofSB9WH6of4CA4IMghACFCIcQh4CIGIjoiSCJ8IpYiyCLmIxAjWiPwJCQkSHja1Xx5YFTVvf/53nUm++zJJJnMkpkJJJkss5GFMIQ9w04IS0BZRSJLMIIo1l4XFETQFkVFBKwVrbuWpRaXPOtalZaCPKu1D2yf28NX21qfQubk9z3nzoSAS//+Mbn3nnvuuWc/n+/n+z3fCxHIaEKEJfJMIhKVhJ4GUtP8jCqRz+ufVuQ/NT8jChgkT4ssWmbRz6gK9DU/Ayw+bPKY/B6TZ7TgpuVwN71Unnnm0dHS24QQRSACUYis8XyzST6xEAch4LF5ZJsnKkc9NsDDj2ETXgUikT4iaClNJEBSGoZIP74qa+l//YRfKB5EAEyj4g/ztWBZbslcIEjucqHATOpjkYBXsYo18DNYeOQI3UMvonuOHIHXj+/YcXyHSs7FLGQp+o7sYA8IFq+BpmqKhtk6SDEZinWVWfMsHlLfIkRCgjdPsLpAtMlRUu8CmzVP8HlDEInJmkC+wcbihT54cN/6cePW79Mv/f1E+MUT2zvCM68cOWt7Rwc2pk8TNQ3IWW0gEbuI3yxI7KW9HdtnjbxyZrhj+xPbWX0EYhjcf9h3Jg9gldjBfhLm1af1ERF7BTAEmoxngQDeU35mB/YPsDiFtU0gxChgX2tn8S6FP3zG38O+zMWEVkU1yaYQRCMxt13WblvTT9bcdgpaTsnahlcqUp9owt0Vr2zYc+oUHwN8S2FjwMYV62PNA5+pPhaFc0EP4JhuPr2la4eQCVCsNRvnLac3A9nRNShIBFZPXpciEmHjareZsEbRWNTEBhVvHDasmyniwP7HJ+4AhlsgbmOP7PUsWVA8DFmHuzoSa3avSXR09XZ0HaZfHa7raOARKjm8kWoLdwfuamwHbcqaNVOo1t54V2D3QtA2nsQL1TYePrwRtMTaWUWYhvI0gGlYz5FeldWtgPiwvfW8bpVgAk/cwxqtR/hwhHxeVq9YWNG6duzo0miCHtBgy55TlN/jbYIHFGwyi6IJ6NVO7RG0c7c7ugBDRITMuMlYqovNAFYeuNg4BWPRSBCDBRhsEaKRQJCl5mOvSfmxpqbY3GQSCmYvXjy7s6bVP2WcjI/P4iEUxG7ddWt0brKrC5/P+Yz2fTans2bNjWMvPTwOi8B2Vhtw5pEr+cpyCWabVVAkVQngpGDFtChYcIsQCIYgT1ADQUUNifmQB7g4HIrN6pIdiponhCAYkoJDMd7ucEkOlxK32q02qxIMlAewtuYWQVwLdsg6+fyNbcufpfRunw+CruicxZMm1JYsV4zGfIuUV9+8OH7VzTdfFV80IpSVVZBvMErLS2rHT140JxrJtYfGjRjrFIyl3liplFNkNDlFY6nTmwuKwx0fu6gZfL67aOrZ5W03Pn/SQNiZfrXlIfr62RfrVXeh9JvpoxY4FUt5/eRFm2bsvTy/YvzFdSDK5jq/F8DrrzMpglAxtSFekt2zZ/rmRZPr/WYl1JmVJxdEq6VcX3GhoGY7zaAUuoZ5pNwhrqF5WabyKXVZhW4l/MJZaHhoC28cdiIDKkJ4nxqIiZQittSTBJlKiL8+LogKUe3+mDleLrvAjLhidsRIPBDMAda9LsERkxwCsETlccHiVXx2S4sUD1SBWyIIewRxjzDgk8iBw54n/0w3db0rjt/1ViE9TY/nNXaeue+KFT+Cxz4uSNCP6Bp5+biD/9dsLw0qj8DEq51nG1+if695Cb68Zevjbs19yW+VvZO2LB9yLT1Er4JdsAEsP/85/ZxupEvw+PznPweLNhWq4MY2evS13r0roL03FCq+m/5W2Jx4iP5u/dsQm1SrddTDuw0Xd7lKw+05HqUYSuGfM+nhE/bxIXBCrGAf3Sc0ultay6/9qXZB5lggL5R1FyAeVyEef0Aa8EZR7Qi4kuRz++3helzyOL0wgJfhOL8YXsXtkgNnaIsQrrc7YvE8UGOqllwpVM/Vnvo9pdvoEdpfVTXzgZ+MuPJ5n99dV/vjhyfPTs6uvwVu+TCrcfGm5OQt4R+tsLY3rFJquycX25Yff/vwfT0jH5QDY+vEbavV3KI3b5QrxfqfXbS445E3s4dUtm1a3Dg8XpRILPfm6vUlKD9UjQQH0MGHKG3xDEcZEXbEAz4UIKUIiyg0zwMI+hHk5dCPKlv3yZOWX/TT2VWUpqrYAxUR4SxB6HwNpN6c5jj8Iyt28drRp2lfqmFHl4xPOLZjufLHWK6b4YPIBAMrI9IiYU+Ugejl5YrSbpiQT1+lvX/+s6N6/EXXtsW7nE51/pKKiNMofU2P9h0SJ0ANCJEFs8bHShVRpB+Z/NVeUTASRJ9M2yyIzB6yhKzi2GA3s0HxeXFFF5hjgDMXFKjHuZsNdgtYYvEWMRphQGBA6AjXOwLlPq+kqPXh+tgIiNkVVVHBIiKOxBz2c3F+HGpVjJmjEbENVsDEL7aN7Nn38idXH6T7v9i27Qv6pzNv0x+PFQO3XC8JX/+j+y/gmypIBXkW1VFoBYdslvMkVZjcCMZV9NN7b6H9R8YXF/lX+Lw2S561qhb8T13bbs23WjdOCVzm82GkrVLwycO/OvSeqmHu+w9e/cnL+3pGbvsCJvLSU3mn6YYlUul9fTUhWREeSo30SHv7dkOOklNXNzZcGJoT9Qp+gzu7JL/Qlt3QAUu6Ox9YJQsilHlFWei7SzDBbFXwuiErE6lWVN68M9XQBT3vH2FzXSC3wj9Rlm4ldWQ4G0W73q8hITOh1ZARh5FBLM5+Me7xh20+my/qi4ajYeE9IZAbGLPkmh3T1723++JF9797+do3WncKVqO9oMjucpWblz66ZMmjS0d2j48VSXS/uE9nVJIWDE/fcc2SMYGLd7+3bu37uy+ePPEeyFVzDdmqURIXP/rbRxeXx8Y0Fb3Nk2M9RZ13Kc8jJzFjXTkjCTJxx4YX4R/FPkZF2FQHFYWyxxz02FoUfCbYhPn0ILQ9KExbumxGvL0KqjrkAnpoWkfluKG52fSQJMGEbJvbUxNuLZ++eVkDEPG/bl40oW1h9aS62kmhszsF8/Ir/WF3cSz1n+L187eaSnzFxZbs+GWPr2ZcKT0/Gct0k+ZBKzC91Bg/saCYDoEPiYTVjhG8moIa9dgLbCrWOs672mbSVyVbeCiGHfSbG0ZPg6mto6ZPGyk1PbSpftowbwH9GgAMhixvg3fMyMwy1ZfkGSIW9X0sbpzS2DxpclPjlL4N8NqTB4sqg4XdHtpz4CAcrrQ5h5Re3E5nY2c+isJhGsqFqazGLkkf9kBQwJURDMQtbALEWKWsrD/ZGsFVEULemYdJkQSpeewvyOeJLNWt++MT2xZEqmdctePgksVPeicUeOffqZb+TMqzb71kxuxAc57j6iVrn1005obXfzT/0ZtXTQjOMKuqaBVUn33munj5xBV3/fIvBhJftGnvgfkbPnxx18rm+Qn6wbAN22MPXy08ZfQsj9x6+LLp4e3/0bD49l9B3cFLn76uLTSt+6a7p965yOYszJmSVWgy+u54rnvS7nu3rp9Vr+N4RvYtzvCJAiFPwGYGY3ELn8/AGiXqjbI77AgbEI8Fgmk0x6nD2CRS7TinOWxuYboywE5yBMiFXCIt5+/YliwZX7J12lW/u31a0+W73u5Zd3T3tVOGdC0zl8iCSZDlvNHjtN41Sx/oGjZ1x0XRdn9Odp1r3KjY3GiBwbjG4pAP0NO7BjMH+hn9iuU/dP1icEaTlx0G8c7Ox+9YnYhfdM3td7bdcmyoIc9iSGRZbaYpVy185uZpzctvm7n96zujndGaXVcObZ01+upk5TSLhfpnLNo8BRyw7sgAQRDIXmGBukDei4srn/PeAuS2BeXpq2yF2V9+SR/+MnVFOiDvZecv03d41eUlUW9Xc4gXbyQR+bkP0TuIkwWpYhx/FrPDjCITQxhlVjaAtSAHlaGfpu5bsco7bZ71qvaN1z0152hdxNo8YdiabkPBpsSYG1VioA/SFB1Oh0AZ3HYtlLWvuKLnboOV/p7+agr9+1NPzbu7FB5nbcjoT/mIDd9af0ZBIag27OnjZ+CanoKsl/J7Ac99nL0SgHeJplTgWvbqWgUqEw47kw9xEwoHnDaMeEZNvihvVFwaBb+gs0wF1c0TN93cM3/+ig0XXzSqNfJqVzIZqjapGm2iH9PIrqoqZ/ls+lHMbi8ra2i8boOwNuVLJObO2cKm52D8cJBqjsEX1J+4lQK7O1aANeKr0c05B9bNHkb2b8J5WQlepRSs9iaojw2GELGMvnSKqVBIzf/XvPk0/ez0ZjP932RUJtFkMqqlT+ejCCWn9Lf6TolkbCMqSKg7NY1JsVekA5l3knxp9QOooPSTbeSnZAe5h9xH7icPkoeZNodNsNUq7M+q1KHOoNQpqpWdFBsDFOxOJR9A8QahtgYCwdpANKB3byAYCfIVGIhiZAS7IFobi8bqIqzPo/VxftV/I6A2DrF6B9Ta62rtYbtj4GdjRy37szqsdXYwyXEjOPyyLQ4mv+qPB1UjBGV/VFVx1Pk/Af+E9BkvqVZThSnVCiLgdBZZrADn/RNgIDGKVuEFTC68AAIM5JHOCDArcH2cujJ19mNwpV59EO6kH34sjPv000+hUpA/ph8KjQ9K/5AlWi2oAkjsHVaowIpM54D5A63OzoFjLPt0TUX+HC+AL+GLEhyTZAFkEPCWHew1ngE7H8vOptXpFop6jqwMlgzfgCn07Rd3wmz68M4X9/5pVeoFiLx47+Rdu3ZhaPbOF+//06rz56oF5dwL5GM2V5GJFaCO5uaqVQsSYVTXBJQPDrsUV9I8AjEVgXUEMEzFFKiHWTgDUxiRRmStjdQhVQuUsyj+aoyBcAgUPUI4B8whIRjggocnY1Qcc2MP2T0TSiIqi0GO1w6XiLfsjfStAPXlOINQiAVZlojhEpYZDJjjMYyPK5KCcG+2SxI5yJgfI2T0Dkb8OAc8tpueWLlyidW075r14N4wIbn6rTtmlSdC2KNGEUb+/OVlD4Brodt/KX3/dnHo0I4tV6xrn7vgyWuT2V3tl9AvV14xvCXLsHPlqv9qanEkQxs3RTsstnBBVbS0am4gEDEYzEUFlfXFzki1udghK5VlFTWh8bmohxlt9jGBwFirTTYbi70V9spOj9cvCh0bW8Mza3Js5qmXrBtWPjJsKjaaHRsebp91+0y64TRsuqRp1o43eibdsNAZG9/TTQ899BD9dFxb7qzZUP2MyXwv/fSNdde9DyGdd+rNZLQzzUDvMqxdfRn945139E8Yn9dgm739re6xm9bWY1uzBEiuaLp1Q7j62jtTWaNuGtYz1FfiTV775ALhshdbJlmbWpZfds3637g80+d3fpgMV1uDwxcsnFlcWaZm5zkc44YMbfc4PBZByHGai9v8/haTXYFhlQKUTSh1eQSo9Pnag1aP0yIZi8rcc2pHXhYy5Yy5aHU00l5tsOfVDC+Pb2ieclU0P2flA303f/3WTTeuPXrvZVb3yq3T7qJPrN/QXer8rz27YOU99/7BJQk5t7xL/7x7H/3D+9f//8R1mT73Y3W4ej25BG9cuAjy5BAqSKY8A858HnIJsTiKJ5eI+ngspPiC3kAeJgOXWAZqSMLF0iK6RIe8Wy2aMGb26CZnXlnlitVXdl86K2E2I+waTFa3P1IaWdU+xmzxjB41rACGKdbEiNmTpo+oyxLKW6Z3zpsx0mKRCsKR5NgZ48aXFBeJJmeR0XhKdTQOKc0eP2rMww899bO7N8xzqkPEnKH1M+ffsO3QojmbZ8Qtcm6uqtD/EVS7w+3yuUqzzUKRKycXCr2VeeXV4jOpjwQ5W5It1aMuGzPx+s62Km++ASFJyS+sCCerqxdMm9hYlZP9htG9fNWD9786b/LlTW4hr6QoKz2GiEFXIAYNIddh79hVbgwNMqiRUCwy5iaivseUAtlmBWapCgz+YRqmD9rTgn3gORITJpusg2SINS3zB57bMnQgpo4Mw6QbDiy5auWUiZe//yukq6ZRdZ3r75y69cq2sYteeHB7z4wqekmT1ze8qX368g6Xu9xtKYjEOxdVDvWUOIpqIj5vkXPYsBkzu7ctXzGsIR7tnL1xXsswr6el9dLJ1aFCp8NWUlYV8/pikVlXHrxnVbfYuuzyJQdumNSYN3zFrmff62mfefnGqXeu76xL5lTN6Nn+4AuL5tPftl86e3hzRbDY6bAYjeZ8zCPkLXe7W0I2e3l5dai+FqmIMzhkQtuCS0a3BgMlVrPJ46ofMbTKbvN4orWFRagDJSdNrBkRCnH+jKyIKMzuGGESHXFX1wbwrFQiS+EcJSRUgomjOO94Zp1Gwe6ptyuaPVhkZ0cymmCsgSZGXjFu7lCtt27VwgSoiACeOWMLDAbYG01KpLiu3OAJ6mdM3ZWsqK0QtIvu/3qzbKr2lLTvnD5zrz+Q1Cn927BVDas93KIVJLVkBBmPesxmrGUMq6UPWwSJAY4VYC3TWqK9nKkzCrvzxzidV+0oE1iQWwesdgmsjhgzlyjEqzCzbsRi1e0/gBKO866MXoTpLCimHHILYgXrCtQSgn7R7mD3LpBezx/qyu949nBHvmto/rDbfkL/1hoKjRwZCrXC6HmtrfNaBU9lw5DqshmpLY+C75FH6AePPkY/eOQR8KU+rKiZWVo1pFGuxoEYUb1vWCjvilfoF/QE/eKVtQWllUXrZtTNKDn03/Nks9kGDYXT69qWL2+rmVIn0jOT/vxkycz62LyYaMh3VeZ3dORXuvKHgRJqxeJbW/VzKDS8rHZIQ3B4alnXgctWHOzqOnjiYJdwb03JxOHlDUJ7qCVUnUg9Fe8srq9b+uzGKVM2/mop6n/hkb4Z66oDC43whj07Rx4/pG75HcurJ4Wa6bU5CypCsXlsfSK/Znq6RnwkjuPBjDBM7RX5loUwHDw23VzOu81hU2VPRscKRh1x/aE0ze63e2sA5t03f4w2LwZqzega+bUtW16X7kMaoc7bPX/+7nmw/D6Mlo7Os/ttIS8tm3vPnGjnj0YfPeKpqfHAx5uef3HTZdU/Ptq5a+6cnZ1/qA0dZ/FEryPbP8B5nU/KM3ybb+Lo+jrbxkF+yPZyHBB3IamOOxRkxpn9GyTW7wWSXX76Hn3P35UMwHLZ1DC6wSSr3Kx+VN/iOcrs6Kl9LAF9H/z8hR1Sqc9XKhHdrvUCcqnWgT0WByFG0WTMiduMEHUIt8Ga1Od0O6wULBTDggVWpv4u5NPtqc9hDb0dLt+d+iL1xW61lb5FD0F56lnw0V/RtyAC4+kH9CFxL/0TTIDI2W/o28t66EvQ0rOMt10ghCpzsO0uMoa3XRUFNU9iKoQKeaBrOEwcMr6F65vtb8TNyLCYcqGzMKaZcMuiBxVo+dXZjdbIHFlWrEU1rjMGWaVX5g11Z1vL8suaK4RTXtlpSa2ylcr/dFpLyz6wFouCS5RcFvr3Yp+vGEZk2wtUsmgRpbTFarVV2MyCgTYU5IqyWlkh2xxVVSV09S/tZW5zn0GRcZ4U5jnzDLtyrT5vcbDYk2PhOMX2R9h+0GDtb9BmCPnezY/0bgfHOgFnLd9TYnsdqPw5PDaPGBZ6xd5+wjRETJ7i8jylIRPW+klmLmHJCmPHOdwqZYTMRqCESyFFKBHf7GKApmAwRdg+U5Ldk8weC5+HZcSftmtm2DQza+q7f4hNeCdZTKhsmcQ6cIH8XHf3c/Qs/ZCefX716ufhjrXv3NvZee87a3fRr3buhKw/wdBO+rRKVj+vJ2LJkefji8+fXd2588RnJ3Z27qRf0dcxuUToXPqfnTAV3tPnB9aJ8L1IE957GY7arSLrVQ/rTKmL72ZqTGs+tUfS+B4m/ezUnn7siD2nCBncrmxSTKp0W53JEw3b8LAw45c+rbj+mh4vNlQ+VlhYRqFzBg9NwM5ORvu4xiniOdXrRKYcSODZqWhn2RLStLOYjCVIsbNwIOCkhD2HXkx5fl1cZChpxLrUoqasioxHxS16iZ4mqK0PowJRAnU/VFUJy1JC4RJ1xRO8DMK0KYebmya/s8bSb0AwqFij4pxQETyNVRLcDtTnDn9X5QnJGajr4H3rYpwblaQJZdwohqdhm5g+MmFPOowc1Wb6oZ7OvHtuO5vVmF+/pwGU6GnYM37Q9DVzFsh3NQWi+qY5Xx8zYaZ6tXo1tseNCAcOQB2tRYA4qAFvPt+jUyFurx+BsAt/Fsrmpk6VNzUGvTnWYcLX+4WyA/6uwIFCs7lwf+rkgQCG/cIwnspfU5pnDIWnS88dSJ3c7/cfKGptLTwglGHwoL9rYG1ynC8gJdh3KqCUZjv15W7JjOyOIM9HBEMJhdhHNGq6+9n0+oFhkLVzdd/q9Ue+PLKenQAb/LfVmSe4dHY9eze8mX64fv2AfTpdFm/pBcWRdFGoXtgtUY9NNsHfvlVmauxAngZBE1dT07fKpd+cq5VhsG2cr7cSUsFtVza2FeOJMjj6gXqIOIw4UGzpCv+mOkomIb6S+jf14vKNQKWBKO+QXKxTKaJbNdv/Z9AWNEIMqyIagXe8EZi2FUNVI8aNjgLnXYifMpyl8hL6JfKeL5dSBc4shRwYCjl+WEu3Tnrl3Zcn0lvh8kmvrFjxypQUYWauU/SlhRxbZXyTypf09CyDM3BmWU9PXyVcAT2TZ0yfTG+lW/EKL+3RXzglRDk6n1dn5ofh46uOgDcIjDWyuiOtjDNLeByCFgcE46whqEtk8N7PmSM2KK7zTYkUeWC/ckoAWMBbcucvdm2/qH3FK0lY+8fQdWfJdRpt5M268//eSG3h1YC3u257eAVvWsuaEaf2rEDIgf2eoj2nhJN0L2vTlO3e6ZPhinfhQ54DvMoauDf1Fm/4V13LeRNfWrNgJQdjEBho6b4S2P/M7IX1MwIKo15IaLSX9mqQ4CdIyBfcayxNen+R29HPz8NA+nrFhNbX29eriQl+EhPqBfcaS8PmqJaWKxbEsyjzcLFVGqJ+ziLsKutBhlWIVHJ4wPgZPveTiQ44mo49ySgg0DCB4OxPA76mg4+eQuGJEYoOIOjiX2+KqyACXjMH5w1QirxhBzGy9WrBP5CLQSW0/BD1U/8hWi5M3L9f+jE9mPoUJtL9ggPaQHCkPmXYovMFDbs2i692BN4gMxqj1Ne0PqKJuGAUBpiUGahTvdBLE+f4MeMLRu6TZAT8M3kYi0jhT8TfGQxzF5pedmJVJRLvv16lF98zkDzGdIwCW90OHIoaQfXjfMQ+6u3TaELUUo8vEGak9moLEgs0mIThBQqW3qdBL7acPetbwJ/lskdp/oS5syE2Ztx8VOQ5jPYgDCVS/E1WFegdjDc5uLY5g+a+Gp6IUO4z1aMYcwLeZEGgCnxmphyhmAWi7zm09ZMjdPfvj8I2mAYlr67qJ/Me/Jx+TA880b23G//kjLvE72HREZGsepX+lT5JLz/6BCSh6PMH5/VpPB2X7f3fADEo6ovYG07uo+JCecJ1UlyiLcgsBpZmMXgs6luVeZErZnxzunVZs8PhE76u7L68u5L+H193f4zQj8LC3LHa/LgvMbNrmPTO2AkTxp45ylcVRNmeAQ5MZp/BhtgQ1nkNQwXUXeJc3+RIhqCG6Oth0GB3sMYH1ZAgcBqleJnHFv1tkv7mpVkPbm0E1AoC0S2TmIMOHqi+JmH4S9d/MofFg2/G4i95YyWcSo8dD7U3AWoT/tjwU0IZ28h47PiSOSwCyutLaS3vPd3fivsxVWa8mPLAyzg9Liu7m7sz+bwDTkt8rXGazJ2XOIJrLLRmytRuXDcauzLXpZR2NcP2qxk2MD8lQZuypntqmmy9TJvZnUA2snUBP1HY3Mgjhbp/HIKnyrA+GjGjClHAii+wi+VccsyZSpfT5VPn7IR9Nz733I2Ys0qYNFl7DB/AXVOPrd0FWSnnc2B4jjlTMTxbwPBMPsmWEJIJH8QdMucl9KR2Uj65IEVgr9aLY4Vz1EAGuBQpwsFi48WuBvI10Q82k3GZ4pHionAQZ7CQIZhHEFd1HrMLO0w4iKwJzALi8JjKcIJxDwMTTn34y18E7ZOa0f4/PnTz6UcXrZc3DVs69i8pzfLO+KlLnljF4pRSvP8k1L1xzNP0b1X0jH3zqyDeugvsdPKlrz48Dt+3vDP215euPbKtFBR8SFNMJxGxrZLGW8OWpcb87tL1ZPjDOoG1j89EfzrFWVRP+vC9PsKd3RjSzBASBtZnKtczy9gq5/wgfQGHlN7vM6fXizCM/gu2a9QCa6UH04HuvlE4Mdgw/H33mjW718j30zLEJyLsSZ3Sry0L2VOcPvTwGpbkPG6icj7L8IW7kg1emTL3HUNVCa+QPLceEYnTsSJ3IBu8GAnLisuUdN4ZphzXmTJJ4475gqs/7f2pM2Vd/Mhc8Hi4EEK1Ecmzz8TSCPu48Bj8B2nnRuZHmRFDNKGrA/ycwMqx5zgI/A3QX6T6ZZ9OjCVOm5lE0nM9yzVK5oTKCB0j4kRlumgJ12d1cRiJNUHajsVtTNw+OWizT1UPb2xdVxV67vI9pwolwvWyHWWejYfD1Us3nNrT0srXpqaCKqf9Ye1Wxr+DbGEEA5ERbCdNRFquHEwmP207mqQN9CS8Bm1tnyaPt83e20/2yruSx/ARjKcN4GaPjuNdW2rHXiAMkIHJLpnRKPVc/4t6RWS9Qtym+Af5f+UnuKwRsPCoByQCn1PLLJjFXFTpL+THqYVaOmCWBrO4HRIX2B8UTX8H1zySWyS1EplFf8G8UGHWLGqRH++gv8B3O+BzrssnFFYPxuiYgASEiFRvCllNr8xksYDUJsHTMSxJsHRYFyMm41YCIYE/jQlsDKZ6B3wJRKwe88bEGSxyd9o+Pg8BVyhWTX+Gc5st0syzNE+QNe6STIwiq7zGSBmbAWeJoDsecx5fwG5kTfm2/ucjQZzZNShz4lwTJBl9jx3xsM03+D48SB/8vnthgEylMqE+7cLAgAN0xgP6e0K8awRuB+G2DFbnb+1iZ5CF4ZisG2T4WbeNMEMJs5718TiJObNo6dUu4qM0jvD8GX4FLsg/zASuzRcdVI4YZYownCtKYxlpmQI5K2NWwEyZqOExxfhcwQeYituv2xAydnCGM8U6FjN5Lqev4LEKCiOAIRBEfIc3iF/6cJBv+vQn/eQnn96kcODglnD9mnrzbvqvX5bSf0Ju6S8hm9FEoq97Ja3FMXxOAwBDq8Eg4IIBFJCwesz1FnDe8NZi43SHX0U5vLGqfVypDgoCVk3HLmBmGyZH8OJ2bzzsqHSlMeIc9pQPYI9ej+8rPe1JSDJ10If1/JI5HOnQ+R1lCtxfn/EqI7fgmdjWlkfl8hqBGDECFy3zLmf6JzNHpN6bKwToXIGNEMV1xy1yKMD38Qfn2bDymZgo5c4cePJFue86MKjFNP2MZbNhuUpNsdXI8gaUm/q6TY+5iY84kxBNyGrTs5nVLRCJc41F4apFIjN1+4hYX1/fd4TZo9hU0vT5fBZLi/80zjRNAdFyj7pAXUCq+M6K6ldUixpkRDFoCQTlINMf48G4HIuLcQeictwh2h1+h2rHseaT216vLmikv6tptm95Y4Sz5Y0ttqZa+rvGTwyGTxqhrrbJtuWNkdaRb9xqb6qFOhZNN3H4FU7fam+uOZdSzyA3O4E5NNfoST/RM771dcy4jGM3ucDGYEV9/rwvH4Ab+VWI+fnOaRyUC7+BkOo3n96yaYNweHwf4aHUmPHf+iAidWTL6c3jU2M2bGJX4fCGb/GH4nNypTyjVyCgstXPlrusc4eUfmEsCGGYsEkj4ezRY/XF/SaTwWx1n5srOo8y6SyRxWZEvUx0qGbceoBz8ZTsyxH965GBbxIyOK+7D4n48AwrnmTwftD+QyYtkiELm576dyB6iSkuIAa+nyCDvp/A0tLfT4jAHbwN34u5ZBDm6kbwNNalQRc7x4AAeEZfsXj+OgO6vKoixyOWv4LaFcNcjqnG84rxpH+DihPS4CoMFAm82rj0M0XzL1Gw/0UtUzy+hO1mrR+oxoXzznLhvJMym3TI1zy2MDK3C+edsExH+720V9v7rQlXz4vpSzJooWk5dl55ju/+wodx1m995ZMazFsvKOjskfP0yPPKCH93GfrONa4qB9+uZkDLfqUQjnIPqO8pH170t7ffsf/n825aUlHkLCyKjC52vmUyj5n+fXUSGhqndSdGXrR/XEFBia+k2Du0umpkg7fUaquOpH3hdZ1Xn9Xsp+K8YYYKjrknqRuHzQ0nL0jLEhpZ2hSOvESYwZ6lZcyHupk9I2MHYUzHTOz4RhgVg7AFj6DPb0HNLlzMggqjGimWeQe00/85UamlPuvgtkitYwTeybwu3I7JE6bDvO7/xPrkKtvYTgbTQFsEexnEW8CF0horv35CU/DGZ1+YcP/9E1741caK5gk4ZZeO+c1r97YMHXP33WOGttz7+ktj2Jwgl8BJdafixhWsfw3F7F8iqBbRwQzaQeGyE/Qo1Jw4Kh09cfToCag52/U1kK/lhm3IoRu2QQO8to2+Rl/bBq/RshaJtDCdjOunaTtQEdv9MQpRFLSoxX3LgTjKtTREubBJNxIpiCqsnX0oqges7lEm33UTrcxhhFnz8IRU9lwKbtMfMPp+ux6lP1wP2w+Xn/p3JWvkO8os+4EyLSj+g+oPldoHL8+lOw50/lDJOH1e7mSJGIqm56iMcgzLNRkF5rRgCqIIY/Y0k8CtngyARYJyaEfbc0v6OR7LCWYdpb18CrMPyujxHW0Tqabfp/0ldFzP4z7Vg3OVL8iLfMf752wPIuuTjCzycgdl0Weq5w4WHD0kPsnHrk4mV48dt6Il3ODzNYRbVozjMcB7SsaVxzRSdogDoUEYx/lRNrPSQBrEeYnMv9kT5Fv1wC0jDLgljS2shmHdKdLtDxcxNS/FxaPE51EfSW6Nr1lTPvfiem0wd+K2hguHlDkEurFzZE+Uf1qncEW4j583nwb76c1slxR5h3TeGGq6J6rG6SbTNwQiz8I2FBAn99f1cJRUVBt3QfF5mCmOQWglFOlBH8qkZV+uXr1w6sqFf/0NnQbk+iVz6uouXbt96YK3FG3smHuW3ZinFt20+r6nhV8NH9daWkpb6PFJU28jaTs6kTP7wz4xrHriYYsv7pFna19oFTRRwS6oXnKFikvOtM1b49wim2EQ6+eMYwmYgswRk7MLOJCWxzhxe/s5Vko6Xel7U0j0phaAm00QI/ezZv3KeIOR5HB/ZxuOIMp+i8ljYR8asNk2BEC3DKt+I6BKr+nKDWjf8DHTzS2gm5i1bzROhPFeThNjiqVnDC9shEHjLErjagYztmnny0kz+Y/zZZgjqKgjuLtlMF4j5EONMEJ1jIAyCNRAvhQcAY54cIQQCKoO/MsXWSK8RVkXR3jmCeP5QhnGYaAM8iGuloEazzcEK/HGEccMJYdaIyvMXdNRI48QkDiPEPBtScWkIuboyMdZd6GIzBPFLNnkEsjLkGhT8n1FhcMiFUEAWXbkWnL9geJRzsJch5xX6nCGC8XcGkOhrSJ/Yo9k9Ug2Q/OkZqUgJ2R3j3FdtuidJwO1bl+NSynJrk2Wx3ODxV6Lx2MszbYmY0PlvOxQgbMsz+fMcjsNhaFgnVLamD8kWIUKowEMcpYMTtc1726SsrJHubPUPIMh35rbHBTyLaPrvEaDx1BTWyY4Suoryk2CRxr6LcH9L0mxIMPum/zHp7LCRQaLTSyNueOq2ZdndfogS/VnNcdkVbD7so0VTtHuNNqz1ycFk5wlGLN8pc0em9VkMIH/ZsgxGBTVLDrkItvQfHOJN+AwmbPiVos9x1SgWixyvsliLXQ2O2srKt2uSqfRPKW2oNWUZcpxlIcWz/gJ7X+mPOeWEa3DSgqiLXK2Uc01Fxepdq9FrjMWZEuWxpGjyzplh8mpcBm6V3SrC6SMDfJbPH6Az/t+fcMNv75BFAdfpJM38Ougv7SfJLO79DJUxzlvIF9rYq84YK/BGwNbKyRqArEXUb8vwd6REnwvC+ORa/BYA+lLcDtOIr3PJXD+wqL1PAfbACpILRmmf6+sey4hJ/Po3y2nv5YxIWOLDYd0VHl6wUtpYodI08i/Ru4njWOZLtwYuPqmrh083KfvRQrJtMPI2LXeB5jc6NIkn3fdGIZ8oY5WB7WP29H1gHftWIyw87QHMoRZGdAtzv/2PS1LMps7me+4gejSpI8wBV5EAU55jMhAgmlOeFCSCQHnYXqY41ucY4BGcvX9EKOIOjEEWyS+Y+rzBiEaDCj5oDBfLodubiyDcyYaAp9igf/0+8EP3MtP/G0M2xGjBxPOTv9Ef5c/X9Dy/RjKdya0p6KBQNSvatSBtDPX3xWAclG2jZu+8QyNTkx2xaBNSzjzMbH+VheGOp2J1L/wJX+UkMHfEo4mE0k7mUeW8D2jtE9gC8SZU6DHNBDDfGzZ8A6KiHLlf2C0mdUHrxlQH/D8ueCqDgx1Mpoe9rGN/Sjx0kG2m5MOMiealD4N+tJq2vmX+fq484nwAJKqD9L3Y9Z5wZeMPpCeJ3j7wJ5TkJk2OJPoB6f2pMXKmeQgZTiZmTsC9skpNaH08v00ou/Lh42CiGzXwbZHM2tWfsS3plXMFmh3v84k6fH/Hsc9A/Cnb0TJPdEWoe+kwGcPqoOzerYxkxi7F36W3sETYBWuqZ/imvLwvRYH9w6Iu8BhYh7XgzrZFrb5TC2Q6WaZ3rGMPkCX0AeW3TH2lR5NS/edpvW8Qn+kd9OROY/+9s1H5rRdYoF/aQ+c64UHNJptWSqm0o0W0nOCkMk4H3SLVyX75tdcCqytwyESZFt85UFlIMIcDwR9ujUsEg+YeC3xoUtwtwjML47dFah2m98bCOreoI48QeWbBG/neucuCkQC18+lX+28h/5rzg14s3iOJ+9t9rS39D68XfrY5yB9/thSDO4qSWk7U8Pn/mNT5+M/aarY8mu+qTCybRnt38rzS5x49MpbNl/52HH9bivAsgmtmGTqgiMg6HHXY1aY5fX6He0/0tmh/WLzwpXhzsTcWyZnbF3aoL1swZNGC1nTTXps3TOeInHGwMaQMgSAAQ7AuI09bPJWAclCLcHqUO3EIb9+371H6eX0SfrXV1cJpOv5S6D+sBgOU7LqVSiBabDt6Ocnnn+a/m06r8OrOBca+f8FUcr9zjhX5CTaGg8rAjOvBoRg2AXumDR1z5o1UyJzws/2Wr98up88/aW11/EOFB8XtTVTBDJlTXhOhJKpBYfoF0PoF1AwBAoObT50KO3TLGJLB++pySS9p3buO2pHxoLDDZ+mwWE13SeDzpxAZc6MOn1XPKTfy+gJvL+zM9+Z6T/mLsDwltnSGbHWQ6y/+TduhNfNyHbRQPTIoh//PCIKMe654JHIOroVqtahHh25Eqro1nXHhMdT77yTOpE68U7qHeFx+WN6zx/onvffh4V/EFENodekboRb6DrhGrgx8917poyMP4SnGFCFH5TJsWOo7g96Mb0ZN7h++YPfFnklL8zjWKaK386MVrD6wbK07x7X1ezI8CuZ/cmIs4vtZnOc9nBvczbv1EAQYZk9hfq43cFs1gof036udnWxweCBueOHzLphj77r20f0O8q4MQcyLpaBpP/TkKZrF3Xq8ZSH4cLv9arJBLLoO7029Z3hgId9i8x2j+3hWJhv3NnjulJSnv5M2Wp31PNHkqPebhl4xp+EM0/s4njohol/27r1b3Q/vZ3uZyGxy+LKN+bn/Z3+NXb1xNEmk6nI6cz95SU//uKiXK2kPLiJPvPIuFunjA6HyhSn0vPLn0OgK8epuWrCd9Dr3+l7JBEO5Lvlx359GGZfXaRqg7OGiby4s8vykRcX5qlbTWaTIbvYbHPlOpsacj6qcTVYJ8/GEk3NJZGs3GDbqFxwRvxh57xZYduYQDg3MCWZc15fidybtIjNdh//TwL4ZrzoyzARWxxn7y6hZFffxcpwWk3v/+yvlChLzpyFiz+Fx+THaDUcYwccP/s8HcUIiPR6apQ45+yOY8c4DqVtSen95cHaJhPPusJznmcmV3XYyuQx/Pz/AAfdhq542o2QsWrDMBCGfyVOSjOUDn4AdSlJiY1sMCTZ0hQHQqcM6RyMahsSKVj2EChd+wgd+wZ9s7xDz4pKl0IrkO7T3a+73wZwhU8wnNcNHhwzDPDiuIMLvDvu4hYnxx4G7M5xD9fsyXGf8q+kZN4l3e7tq5YZfDw77tDcN8ddPOLDsQef+Y574Cxx3Kd8gQU0DjiiQokcBWpwDJFhRDGGQIQEY+IV6SQU0RwGezR0GpvBQh+OVZkXNR9mIx6LKBnzlZaKz82+MUaSZGmV0k7JqJOit1hKJasy04p4TcWcmu6wJRHWMm92W4LUimsbK1JIayskYxwz2r81PlciTBBgSvv7M5BqVae6yiWPQ8Fn/McAXaJJMA1a8/9wu7FFQ2Vtf4mwE0IbW2fYyMqUWnEholAIwf/u+QXtVlqxAAAAeNpt0meTFVUUheH7DhkJEgQJgpIFhdvn7NM9gxKGCZKzKGZyUHJGySAgSq7i5wrFfYdPdFXX+tRP9V61Wl2tt8//rdbh1vueV29eWl2tYXQxjOGMYCSjGM0YxvIB4xjPBCbyIZOYzBSm8hHTmM7HzGAms5jNJ8xhLp/yGfOYzwIWsojFLOFzlrKML/iS5aygTUUiExRqGrrpYSVf8TWrWM0a1tLLOvroZ4BBvmE9G9jIJjazha1sYzs72MkudvMte/iO79nLD/zIT/zML/zKb+xjPwc4yCEOc4SjHOM4v/MHJzjJKU5zhrOc4zwXuMglLnOFq/zJX1zjOje4yS1uc4e73ONv7vOAh/zDI/7lPx7zhKc84zkveDnqwsljg1W7bVZmMrMZZjFrszG7zZ63mfSSXtJLekkv6SW9pJf00pBX6VV6lV6lV+lVepVepVfpVXpJL+klvaSX9JJe6njZu7J3Ze/K3pW9K3tXbg9915id/wid0Amd0Amd0Amd0Il3TueesJ+wn7CfsJ+wn7CfsJ+wn7CfsJ+wn7CfsJ+wn7CfsJ+wn0h6SS/pZb2sl/WyXtbLelkv62W9rBd6oRd6oRd6oRd6oRd6oVf0il7RK3pFr+gVvaJX9IperVfr1Xq1Xq1X69V6tV6tV+s1eo1eo9foNXqNXtPxijsr7qy4s+LOijsr7qy0h75rzG6zx+w115l9Zr85YA520l0Wd1ncZXGXxV0Wd1ncZama1x+EcTsAAAAB//8AAnjaY2BgYGQAgosrjpwF0ZcUq9bCaABTzgdAAAA=") format("woff"), + url("/web/20220430225703im_/http://blog.boochtek.com/wp-content/themes/twentysixteen/genericons/Genericons.ttf") format("truetype"), + url("/web/20220430225703im_/http://blog.boochtek.com/wp-content/themes/twentysixteen/genericons/Genericons.svg#Genericons") format("svg"); + font-weight: normal; + font-style: normal; +} + +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: "Genericons"; + src: url("/web/20220430225703im_/http://blog.boochtek.com/wp-content/themes/twentysixteen/genericons/Genericons.svg#Genericons") format("svg"); + } +} + + +/** + * All Genericons + */ + +.genericon { + font-size: 16px; + vertical-align: top; + text-align: center; + -moz-transition: color .1s ease-in 0; + -webkit-transition: color .1s ease-in 0; + display: inline-block; + font-family: "Genericons"; + font-style: normal; + font-weight: normal; + font-variant: normal; + line-height: 1; + text-decoration: inherit; + text-transform: none; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + speak: never; +} + + +/** + * Helper classes + */ + +.genericon-rotate-90 { + -webkit-transform: rotate(90deg); + -moz-transform: rotate(90deg); + -ms-transform: rotate(90deg); + -o-transform: rotate(90deg); + transform: rotate(90deg); + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); +} + +.genericon-rotate-180 { + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -ms-transform: rotate(180deg); + -o-transform: rotate(180deg); + transform: rotate(180deg); + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); +} + +.genericon-rotate-270 { + -webkit-transform: rotate(270deg); + -moz-transform: rotate(270deg); + -ms-transform: rotate(270deg); + -o-transform: rotate(270deg); + transform: rotate(270deg); + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); +} + +.genericon-flip-horizontal { + -webkit-transform: scale(-1, 1); + -moz-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + -o-transform: scale(-1, 1); + transform: scale(-1, 1); +} + +.genericon-flip-vertical { + -webkit-transform: scale(1, -1); + -moz-transform: scale(1, -1); + -ms-transform: scale(1, -1); + -o-transform: scale(1, -1); + transform: scale(1, -1); +} + + +/** + * Individual icons + */ + +.genericon-404:before { content: "\f423"; } +.genericon-activity:before { content: "\f508"; } +.genericon-anchor:before { content: "\f509"; } +.genericon-aside:before { content: "\f101"; } +.genericon-attachment:before { content: "\f416"; } +.genericon-audio:before { content: "\f109"; } +.genericon-bold:before { content: "\f471"; } +.genericon-book:before { content: "\f444"; } +.genericon-bug:before { content: "\f50a"; } +.genericon-cart:before { content: "\f447"; } +.genericon-category:before { content: "\f301"; } +.genericon-chat:before { content: "\f108"; } +.genericon-checkmark:before { content: "\f418"; } +.genericon-close:before { content: "\f405"; } +.genericon-close-alt:before { content: "\f406"; } +.genericon-cloud:before { content: "\f426"; } +.genericon-cloud-download:before { content: "\f440"; } +.genericon-cloud-upload:before { content: "\f441"; } +.genericon-code:before { content: "\f462"; } +.genericon-codepen:before { content: "\f216"; } +.genericon-cog:before { content: "\f445"; } +.genericon-collapse:before { content: "\f432"; } +.genericon-comment:before { content: "\f300"; } +.genericon-day:before { content: "\f305"; } +.genericon-digg:before { content: "\f221"; } +.genericon-document:before { content: "\f443"; } +.genericon-dot:before { content: "\f428"; } +.genericon-downarrow:before { content: "\f502"; } +.genericon-download:before { content: "\f50b"; } +.genericon-draggable:before { content: "\f436"; } +.genericon-dribbble:before { content: "\f201"; } +.genericon-dropbox:before { content: "\f225"; } +.genericon-dropdown:before { content: "\f433"; } +.genericon-dropdown-left:before { content: "\f434"; } +.genericon-edit:before { content: "\f411"; } +.genericon-ellipsis:before { content: "\f476"; } +.genericon-expand:before { content: "\f431"; } +.genericon-external:before { content: "\f442"; } +.genericon-facebook:before { content: "\f203"; } +.genericon-facebook-alt:before { content: "\f204"; } +.genericon-fastforward:before { content: "\f458"; } +.genericon-feed:before { content: "\f413"; } +.genericon-flag:before { content: "\f468"; } +.genericon-flickr:before { content: "\f211"; } +.genericon-foursquare:before { content: "\f226"; } +.genericon-fullscreen:before { content: "\f474"; } +.genericon-gallery:before { content: "\f103"; } +.genericon-github:before { content: "\f200"; } +.genericon-googleplus:before { content: "\f206"; } +.genericon-googleplus-alt:before { content: "\f218"; } +.genericon-handset:before { content: "\f50c"; } +.genericon-heart:before { content: "\f461"; } +.genericon-help:before { content: "\f457"; } +.genericon-hide:before { content: "\f404"; } +.genericon-hierarchy:before { content: "\f505"; } +.genericon-home:before { content: "\f409"; } +.genericon-image:before { content: "\f102"; } +.genericon-info:before { content: "\f455"; } +.genericon-instagram:before { content: "\f215"; } +.genericon-italic:before { content: "\f472"; } +.genericon-key:before { content: "\f427"; } +.genericon-leftarrow:before { content: "\f503"; } +.genericon-link:before { content: "\f107"; } +.genericon-linkedin:before { content: "\f207"; } +.genericon-linkedin-alt:before { content: "\f208"; } +.genericon-location:before { content: "\f417"; } +.genericon-lock:before { content: "\f470"; } +.genericon-mail:before { content: "\f410"; } +.genericon-maximize:before { content: "\f422"; } +.genericon-menu:before { content: "\f419"; } +.genericon-microphone:before { content: "\f50d"; } +.genericon-minimize:before { content: "\f421"; } +.genericon-minus:before { content: "\f50e"; } +.genericon-month:before { content: "\f307"; } +.genericon-move:before { content: "\f50f"; } +.genericon-next:before { content: "\f429"; } +.genericon-notice:before { content: "\f456"; } +.genericon-paintbrush:before { content: "\f506"; } +.genericon-path:before { content: "\f219"; } +.genericon-pause:before { content: "\f448"; } +.genericon-phone:before { content: "\f437"; } +.genericon-picture:before { content: "\f473"; } +.genericon-pinned:before { content: "\f308"; } +.genericon-pinterest:before { content: "\f209"; } +.genericon-pinterest-alt:before { content: "\f210"; } +.genericon-play:before { content: "\f452"; } +.genericon-plugin:before { content: "\f439"; } +.genericon-plus:before { content: "\f510"; } +.genericon-pocket:before { content: "\f224"; } +.genericon-polldaddy:before { content: "\f217"; } +.genericon-portfolio:before { content: "\f460"; } +.genericon-previous:before { content: "\f430"; } +.genericon-print:before { content: "\f469"; } +.genericon-quote:before { content: "\f106"; } +.genericon-rating-empty:before { content: "\f511"; } +.genericon-rating-full:before { content: "\f512"; } +.genericon-rating-half:before { content: "\f513"; } +.genericon-reddit:before { content: "\f222"; } +.genericon-refresh:before { content: "\f420"; } +.genericon-reply:before { content: "\f412"; } +.genericon-reply-alt:before { content: "\f466"; } +.genericon-reply-single:before { content: "\f467"; } +.genericon-rewind:before { content: "\f459"; } +.genericon-rightarrow:before { content: "\f501"; } +.genericon-search:before { content: "\f400"; } +.genericon-send-to-phone:before { content: "\f438"; } +.genericon-send-to-tablet:before { content: "\f454"; } +.genericon-share:before { content: "\f415"; } +.genericon-show:before { content: "\f403"; } +.genericon-shuffle:before { content: "\f514"; } +.genericon-sitemap:before { content: "\f507"; } +.genericon-skip-ahead:before { content: "\f451"; } +.genericon-skip-back:before { content: "\f450"; } +.genericon-skype:before { content: "\f220"; } +.genericon-spam:before { content: "\f424"; } +.genericon-spotify:before { content: "\f515"; } +.genericon-standard:before { content: "\f100"; } +.genericon-star:before { content: "\f408"; } +.genericon-status:before { content: "\f105"; } +.genericon-stop:before { content: "\f449"; } +.genericon-stumbleupon:before { content: "\f223"; } +.genericon-subscribe:before { content: "\f463"; } +.genericon-subscribed:before { content: "\f465"; } +.genericon-summary:before { content: "\f425"; } +.genericon-tablet:before { content: "\f453"; } +.genericon-tag:before { content: "\f302"; } +.genericon-time:before { content: "\f303"; } +.genericon-top:before { content: "\f435"; } +.genericon-trash:before { content: "\f407"; } +.genericon-tumblr:before { content: "\f214"; } +.genericon-twitch:before { content: "\f516"; } +.genericon-twitter:before { content: "\f202"; } +.genericon-unapprove:before { content: "\f446"; } +.genericon-unsubscribe:before { content: "\f464"; } +.genericon-unzoom:before { content: "\f401"; } +.genericon-uparrow:before { content: "\f500"; } +.genericon-user:before { content: "\f304"; } +.genericon-video:before { content: "\f104"; } +.genericon-videocamera:before { content: "\f517"; } +.genericon-vimeo:before { content: "\f212"; } +.genericon-warning:before { content: "\f414"; } +.genericon-website:before { content: "\f475"; } +.genericon-week:before { content: "\f306"; } +.genericon-wordpress:before { content: "\f205"; } +.genericon-xpost:before { content: "\f504"; } +.genericon-youtube:before { content: "\f213"; } +.genericon-zoom:before { content: "\f402"; } + + + + + +/* + FILE ARCHIVED ON 22:57:03 Apr 30, 2022 AND RETRIEVED FROM THE + INTERNET ARCHIVE ON 08:38:23 Feb 23, 2024. + JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. + + ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. + SECTION 108(a)(3)). +*/ +/* +playback timings (ms): + exclusion.robots: 0.14 + exclusion.robots.policy: 0.124 + cdx.remote: 0.102 + esindex: 0.011 + LoadShardBlock: 159.675 (6) + PetaboxLoader3.datanode: 143.462 (7) + load_resource: 80.791 + PetaboxLoader3.resolve: 45.946 +*/ \ No newline at end of file diff --git a/public/wp-content/themes/twentysixteen/js/functions.js b/public/wp-content/themes/twentysixteen/js/functions.js new file mode 100644 index 0000000..908c707 --- /dev/null +++ b/public/wp-content/themes/twentysixteen/js/functions.js @@ -0,0 +1,248 @@ +var _____WB$wombat$assign$function_____ = function(name) {return (self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init(name)) || self[name]; }; +if (!self.__WB_pmw) { self.__WB_pmw = function(obj) { this.__WB_source = obj; return this; } } +{ + let window = _____WB$wombat$assign$function_____("window"); + let self = _____WB$wombat$assign$function_____("self"); + let document = _____WB$wombat$assign$function_____("document"); + let location = _____WB$wombat$assign$function_____("location"); + let top = _____WB$wombat$assign$function_____("top"); + let parent = _____WB$wombat$assign$function_____("parent"); + let frames = _____WB$wombat$assign$function_____("frames"); + let opener = _____WB$wombat$assign$function_____("opener"); + +/* global screenReaderText */ +/** + * Theme functions file. + * + * Contains handlers for navigation and widget area. + */ + +( function( $ ) { + var body, masthead, menuToggle, siteNavigation, socialNavigation, siteHeaderMenu, resizeTimer; + + function initMainNavigation( container ) { + + // Add dropdown toggle that displays child menu items. + var dropdownToggle = $( '