{"/blog/20101118-learning-selenium":{"content":"My basic need is to find a platform where I can test FF, IE, and Safari on Windows, Linux, and OS X. I use OS X as my platform, and Safari or Webkit as my environment. I don’t like Windows or IE. Linux is OK, but I like OS X because it just works the way I want. And I find FF to be slow, and Firebug which is needed to debug we pages causes rendering changes and timing issues (most notably causing FF to crash).\nIdeally I want the testing environment:\nTo be developed in Safari on OS X To be able to test both internal libraries and rendered UI To use unit tests to test internal libraries To use interactive tests to test rendered UI To use the unit/interactive tests as regression system moving forward To write the tests once on my browser of choice To run the tests on all combinations of browser and platform. To not be bogged down by the testing framework To be free, or very cheap From my research it looked like Selenium did basically exactly what I needed. And unfortunately was the only real option. There were plenty of options for taking screen shots of public sites (which mine isn’t, yet) and comparing those between browsers. And there are several options for unit testing javascript, but only Selenium did both and could be run on my own hardware.\nImplementation From my reading it looked like I wanted to use the IDE to create the tests, and remote controls to run the browsers. Eventually I need to scale to Selenium Grid, but that is for later discussion.\nTest code This is the sample file that I created to test.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <html> <body> <script> Namespace = { test: function(item){ return item == true; } }; function String(item){ if(!item.push && typeof(item) === 'object'){ return item.name || ''; } return ''+item; //yup this is faster then toString } function Int(item){ return parseInt(item,10); } function Array(item){ var type = typeof(item); if(type === 'string'){ return item.split(\"\\\\\\\\n\"); } else if(type === 'object'){ if(item.push && item.length && item.concat){ return item; } } return [item]; } </script> </body> </html> Using the IDE It took a long time to understand this tool, since I had no background in Selenium. The basic premiss is that each file is a single test case, which is a set of tests. Each test is a grouping of three items: the action, the target, and the expected result. It natively creates a 3 column HTML table, which it can also run, but personal preference is that use the IDE to export the basic test into a different language.\nI am a rails developer, and am familiar with rspec so I use the IDE to run the tests to make sure they work, but then I transfer it to rspec since it is a more expressive test framework. The downside is that you have to use a Remote Control to run the test, which adds an extra level of complications. We will get to the Remote Control later.\nA basic HTML test looks like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?xml version=\"1.0\" encoding=\"UTF-8\"?> <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"<http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd>\"> <html xmlns=\"<http://www.w3.org/1999/xhtml>\" xml:lang=\"en\" lang=\"en\"> <head profile=\"<http://selenium-ide.openqa.org/profiles/test-case>\"> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /> <link rel=\"selenium.base\" href=\"file:///Users/jkamenik/Desktop/sel_test.html\" /> <title>Unit Testing</title> </head> <body> <table cellpadding=\"1\" cellspacing=\"1\" border=\"1\"> <thead> <tr><td rowspan=\"1\" colspan=\"3\">Unit Testing</td></tr> </thead><tbody> <tr> <td>open</td> <td>file:///Users/jkamenik/Desktop/selenium/sel_test.html</td> <td>test</td> </tr> <tr> <td>verifyEval</td> <td>this.browserbot.getUserWindow().Int(1)</td> <td>1</td> </tr> <tr> <td>verifyEval</td> <td>typeof(this.browserbot.getUserWindow().Int(1))</td> <td>number</td> </tr> </tbody></table> </body> </html> There really is nothing more to it then that. The one thing to note is that the test uses verifyEval which takes a JavaScript string.\nNote\nIn all tests the this object is the base Selenium object so if you want to get to the window object you have to traverse up the stack via this.browserbot.getUserWindow().\nWarning\nUnfortunately everything tested in Selenium is converted to a string before testing. So if I need to ensure that an integer parsing function actually produces a number I need to use typeof.\nUsing RSpec The IDE is a great way to test scripts live, but for any programmer it is going to be easier to use a testing framework doing it programmatically. As a rails guy I prefer RSpec so that is what I use.\nInstalling This package requires ruby-gems, rspec, and selenium-client. I am going to assume you have ruby-gems installed already. The others are installed like this:\n1 2 sudo gem install rspec sudo gem install selenium-client Converting When using Rspec the only real thing to remember is that there are no assert* or verify* methods. The reason is that Rspec itself is a testing framework so it will have it own version of assert and verify (in this case should).\nThe IDE has a great feature in that it converts the HTML test into a rspec test for you. It isn’t a great format, but it is better then nothing and is a good place to start.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 require \"rubygems\" gem \"rspec\" gem \"selenium-client\" require \"selenium/client\" require \"selenium/rspec/spec_helper\" require \"spec/test/unit\" describe \"array_test\" do attr_reader :selenium_driver alias :page :selenium_driver before(:all) do @verification_errors = [] @selenium_driver = Selenium::Client::Driver.new \\\\\\\\ :host => \"localhost\", :port => 4444, :browser => \"*chrome\", :url => \"file:///Users/user/Desktop/selenium\", :timeout_in_second => 60 end before(:each) do @selenium_driver.start_new_browser_session end append_after(:each) do @selenium_driver.close_current_browser_session @verification_errors.should == [] end it \"test_array_test\" do page.open \"file:///Users/jkamenik/Desktop/selenium/index.html\" begin (\"1\").should == page.get_eval(\"this.browserbot.getUserWindow().Array([1])\") rescue ExpectationNotMetError @verification_errors << $! end begin (\"object\").should == page.get_eval(\"typeof(this.browserbot.getUserWindow().Array(1))\") rescue ExpectationNotMetError @verification_errors << $! end end end Warning\nThe browser being shown is chrome. This actually means Firefox, not Google Chrome. For Google Chrome use googlechrome.\nFor Safari use safari, but remember that you will need to disable the popup blocker manually and close Safari (for this user) otherwise it will just sit there forever.\nRemote Controls A remote control is what Selenium uses to execute the test. The IDE comes with it built-in, but it is tied to FireFox. To use IE, Safari, or Chrome you need to download the remote control software: http://seleniumhq.org/projects/remote-control. This software is just a Java server that opens your machine on port 4444 (by default) to allow Selenium clients to run tests. Each client gets its own browser instance to run the tests in.\nNote\nThe server must be run by a user that has access the browser and has a screen to render to.\nWarning\nFirefox will only run a single profile per user. If you need to run Firefox concurrently on the same machine you need to fake a second profile. Don’t do it, just create a VM; you will be happier.\nCaution\nGoogle Chrome does not works on OS X. This is because OS X doesn’t add application executables to the path, and the server code isn’t smart enough to use the executable directly. The fix is supposedly here, but I was not able to get it to work. If I do I will probably write another blog entry and link it here.\nPutting it all together By default RSpec provides no runner code and the code the IDE produces is not standalone. This is not a problem since installing RSpec into a rails app installs script/spec. I have copied the runner code here so make it easier.\n1 2 3 4 5 6 #!/usr/bin/env ruby if ARGV.any? {|arg| %w[--drb -X --generate-options -G --help -h --version -v].include?(arg)} require 'rubygems' unless ENV['NO_RUBYGEMS'] end require 'spec/autorun' exit ::Spec::Runner::CommandLine.run I am going to assume the RSpec runner code is called spec and the test file is called test.rb. To run this test from the command line do the following:\n1 ruby spec test.rb Assuming you followed all the steps the test should have opened Firefox, executed a page, run the tests, closed Firefox, and returned the results. Now you can add more tests and have Selenium execute them.\nRelated Research Using Selenium Grid Using Chrome, or IE Using a Grid to run the same test in all browsers on all OSs ","lastmodified":"2010-11-18T00:00:00-05:00","tags":null,"title":"Learning Selenium"},"/blog/20101204-git-with-svn":{"content":"Normally, I would just use GIT without a bridge to another control system, but many companies use SVN. There are just so many benefits to using GIT that, for me, I am going to use it if there is a bridge to repository type the company uses. I certainly don’t hate SVN or CVS or Perforce, but GIT allows me to work the way that I know I am most productive; which is commit early, commit often.\nWhat I mean by commit early, commit often is that I commit even if I only have part of the solution. As I find the other parts of the solution I commit those as well. That way when I am 2 or 3 days into a fix I already have the commit messages saved in GIT, so I don’t have to remember what I did for those 2 or 3 days. When the solution is shippable only then do I push it up to the company’s repository.\nBenefits of GIT include: Being able to share non production ready code (Peer to Peer) Being able to have many local branches Being able to logically group commits (via local branches) and push all at once Fantastic branch switching/merging Rarely will you ever have to fix a merge conflict yourself Rebase as well as merge Rebase is SVNs style of linear commits Merge is non-linear and tries to keep commits sequentially ordered by date. So if two branches are merged and both were actively worked on then the commits are intermixed. (makes a lot more sense in practice then in writing) With GIT’s power comes a little bit more complexity, and here I will detail the method that I have developed over months of fits and starts. That way you can experience the benefits of using GIT for day to day work, but still use SVN when dealing with corporate.\nSettings up the SVN Bridge You can have GIT manage an entire SVN repository, branches and all. However, for GIT to do this it must checkout every revision of the SVN repo. This can be very painful when there are a lot of commits. Instead I recommend only managing a single branch starting a specific revision near HEAD. You will lose history older then that revision, but it does save a lot of time for large SVN repositories.\nFor the below example I am going to assume we have a standard SVN repository at http://example.com/svn, the latest revision is 400, and the SVN username is test.\nFind the latest revision of the repository:\n1 svn log <http://example.com/svn/trunk> | head The latest revision will start with an “r” and be within the first 5 lines.\nSetting up a git repo:\n1 2 git svn init --username=test <http://example.com/svn/trunk> dev git svn fetch -r 400 Now you have a local GIT repo in dev that is synced to SVN trunk at revision 400. And when you git svn dcommit the user test will be used.\nAlways work in a branch In my company before anything is allowed to be checked into SVN it must go through a peer review. In any given day I might work on 2 or 3 bugs/tasks. For each, I create a GIT branch with the bug/task ID and work there. When I am done I use git log -p to list the diffs that I submit for review. Then I move on to the next item, while the fix is being reviewed. When the first bug/task is reviewed and accepted I jump back to master, rebase it, jump to the branch, rebase master, jump back to master and merge the branch, and finally svn dcommit everything. After I mark the bug/task complete I also delete the branch. If the code is not accepted then I still have a branch where I can make the required corrections and repeat the process.\nIt might sound complicated, but it really isn’t. The only reason for all this rebasing is so that GIT’s native merge tools deal with SVN merge conflicts. I am not sure why, but they are far better then what the SVN bridge can do, and will ultimately lead to few headaches for you.\nIn code it looks like:\n1 2 3 4 5 6 7 8 9 10 11 12 git> git svn rebase git> git checkout -b task1 ... work on task 1 git> git log -p -n X > task1.diff ... create diff of all (X) changes needed for task1 ... email the diff for review git> git checkout master git> git svn rebase git> git checkout -b task2 ... work on task 2 ... when task 1 is approved commit what you have for task 2 and dcommit task1 ... return to task 2 when task 1 is committed This style also works well if your boss comes over at the 11th hour and assigns you a new emergency assignment. When you are done with the emergency switching gears is as easy as switching branches.\nDCommitting your changes As states above I use the checkout, rebase, checkout, rebase, checkout, merge, dcommit style. This seems cumbersome until you understand the purpose.\nFrom any branch always checkout into master. This allows master to stay pure of your changes and makes its less likely that git svn will fail.\nOnce master is up-to-date checkout the branch again and rebase the master changes into the branch. Fixing any rebase conflicts there might be. By fixing them on the branch we keep master clean so we are using git’s rebase/merge capabilities, not the SVN bridge’s. There are times this step can be skipped, but once you have to deal with your first rebase conflict from the bridge you will wish you had branched.\nNow that the branch holds the latest code to be dcommitted to SVN: checkout master, merge the branch, and dcommit master. You could rebase the changes from the branch if you prefer, it really makes no difference because your changes are on top of SVN now either way. Once done all of master’s log messages will be rewritten to what is in SVN.\nAll together it looks like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 git> git checkout master git> git svn rebase git> git checkout task1 git> git rebase master ... SVN needs your changes to be rebased so rebase master ... by rebasing onto to a branch it is easier to deal with rebase failures ... fix any rebase issues git> git checkout master git> git merge task1 ... could merge or rebase, doesn't matter here git> git svn dcommit ... mark the task closed git> git brach -D task1 ... svn doesn't always mark the branch merged properly so use -D instead of -d Pro Tips Different user names Use a different username for SVN and GIT. That way it is easy to see in the log what is committed to SVN since the bridge will rewrite the commit log from what SVN says.\nFor SVN I use jkamenik. For GIT I use jkamenik at gmail dot com. Commits that are in SVN also have the SVN revision number in the log message, but I find it easier to use usernames since it is at the top of the log message.\nSetup Aliases Git allows more commands to be added via aliases. An alias can be a shortening of a git command: st = status. Or it can be a shell command that git will execute test = !sh -c 'echo \"it works!\"' (notice the leading !).\nHere is the alias part of my ~/.gitconfig file looks like:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 [alias] # Old SVN aliases ci = commit co = checkout st = status stat = status # stuff I find useful br = branch df = diff rm-all = !git st | grep deleted | awk '{print $3}' | xargs git rm add-all = !git st | grep modified | awk '{print $3}' | xargs git add st-br = \"!f(){ git co master && git svn rebase && git co -b $1 }; `f`\" up-br = \"!f(){ git co master && git svn rebase && git co $1 }; `f`\" co-br = \"!f(){ git up-br $1 && git co master && git merge $1 && git svn dcommit }; `f`\" Notice that st-br, up-br, and co-br are basically all the set of commands I noted above in single command style.\nStashing changes The stash is a hidden place that git can keep changes that are not yet ready to commit. This is very useful if you get switched to another task and really don’t have time to fully vet a change on the current task. You can stash the outstanding change and later replay them.\ngit stash keeps a queue of changes so you can stash more then one thing, but you can only replay them top down.\n1 2 3 4 5 6 7 8 git> git stash ... hides all modified files git> git stash list ... lists all stashes git> git stash apply ... applies the top stash, but does not remove it git> git stash pop ... applies the top stash, and remove it Resources GIT <-> SVN workflow Effectively using GIT with SVN ","lastmodified":"2010-12-04T00:00:00-05:00","tags":null,"title":"Git With SVN"},"/blog/20101205-how-to-be-a-bad-boss":{"content":"Being a boss is a complicated thing. It is your job to get people to do things, sometimes things they do not want to do. And it is especially complicated in the software industry, where it is like herding cats.\nI see a lot of posts on how to be a good boss, but the problem there is that they often forget to mention the things that can and will immediately erode any success you might have had. I am going to assume that as a reader you strive to be a person who others are willing to follow.\nNobody is perfect, so you will probably have done at least one of these things in the past. Or you do then without even knowing. Now is your chance to stop, and be a constructive boss who people want to work for.\nBe smarmy Yep, this is number one. Smarmy people are falsely friendly (think used car dealers). Notice I did not say do not “be mean.” As a boss sometimes you will need to be mean, or at least not very nice. And even that should be kept to a minimum, but it is never OK to be smarmy. All it does is make people uncomfortable confiding in you, which means they will either find better employment (if they can) or they will sabotage you.\nHere are some examples of what I mean by smarmy:\nSuggest that a problem can be solved by the employee working more or harder Using scrum (or any general meeting) to point out an employee’s failure Joke about firing an employee Make any sarcastic comment about an employee in the presence of others Only talking to an employee when you need them to do something (i.e., I am only talking to you because I want something) Some ways to tell that you have already failed at this:\nAll laughter stops when you enter the room Conversations between you and an employee die quickly Employees no longer tell you about issues they are having Be a hypocrite A hypocrite is someone who says one thing and thinks or does another. It can be as overt as being prejudiced (racist, sexist, etc…) or as covert as being passive aggressive. Being a hypocrite is risky because when other people find out, you are just being deceitful, and no one wants to deal with that.\nOne example is saying that code needs to be high quality but not leaving time for testing. Overtly this often resembles explicitly adding a testing phase at the end of coding and then cutting it if coding goes long. Covertly this is often assuming that testing is done as part of coding, but scoffing at longer estimates. Speed and quality have a tenuous relationship. Something done slowly may or may not be of quality, but something quickly is almost never of quality.\nThe best way not to be a hypocrite is not to bring your values and prejudices into the equation. To do this though you have to understand what your values and prejudices are. In the case of the no testing, quality is explicitly stated as the important part, but speed is implicitly stated as the important factor. More specifically speed at the cost of quality is stated as the import factor. To avoid this, it is important to explicitly state the importance of all three tenets of the project triangle: that way you avoid the embarrassing case where one tenet goes to 0 unintentionally. See: Project Triangle.\nBe arrogant I am not talking about being hard headed or stubborn. I am talking about being truly arrogant. Stubborn people will listen to reason, assuming the opposing argument is good enough; arrogant people cannot be directly convinced. There are two forms of arrogance to watch out for: arrogance of idea, and arrogance of presenter. Arrogance of idea is simply dismissing an idea because it is in direct conflict with your internal ideas or values. Arrogance of presenter is rejecting an idea because of who presented it.\nTwo engineers approach you with different solutions to the problem of releasing on time: one says to keep the feature set and stop doing unit testing, and the other says reduce the feature set but keep the unit testing. Which do you choose? If you choose “keep the feature set” then you wrong. If you choose “keep the unit testing” you are also wrong. If you chose either (based on only the information provided) then the choice you made was based on you internal values only and not based on the problem being presented.\nIn the context of work the truth is that new hires probably know more about a given framework than you, and certainly will have a different perspective on how things should be done. They are probably offering you this knowledge, because they have already encountered the problem you are now seeing and have found an appropriate solution. It is tempting to dismiss this simply because a new hire presented it and they don’t know the full business impact of their suggestion. But ask yourself: if the system architect presented you with this idea would you accept it? If yes, then you are suffering from arrogance of presenter and should tread lightly.\nI suggest instead that you judge an idea (never a presenter) on its merits in the context of only the problem (never on facts you assume to be true). And make sure you are consciously and actively judging the idea, and you are not adding your own problem or values.\nOffer post hoc rewards A post hoc reward is a reward given after work is complete. In physical work the effects of post hoc rewards are well known and lead to better results, but in knowledge work offering post hoc rewards has a distracting effect. Work will take longer and be of lower quality. It will make employees very good at performing work that maximizes their incentives, and not at developing a quality product. Be aware that offering reward for work done defines a tangible value for that work, which may demoralize an employee who feels under compensated as a result.\nIntrinsic rewards can and should be used. These are things that are given universally and without reservation. Basic examples would be vacation, and health benefits. Other examples would be a free day to work on any project the employee wanted, or free soda/tea/coffee. These intrinsic benefits help endear you and the company to the employee.\nAccept estimates less than 4 hours This may only be true in software, but tasks never take less than 4 hours. A task is really an atomic complete chunk of work, so to be considered complete it must be coded, tested, reviewed, and committed. However, some employees will claim they finish tasks sooner. Often these people are cutting corners (not testing, or not getting a review) or they are doing a bunch of tasks that should have logically been considered one task.\nEither way they are playing a game with the numbers to get their counts higher, which means that you are probably rewarding over aggressive estimates, and have a huge defect backlog. Well done!\nForcing the Time-box I have seen this bite so many bosses, where they say something like “tasks cannot possibly take longer than 4 days.” The problem is that now you have employees trying to split tasks - often nonsensically - that have little or no longterm vision. So when it comes time to implement you have a bunch of small tasks that are too small to warrant research themselves, but no real clear direction because no real research is worth doing. Then it gets implemented and becomes instance legacy.\nEstimates of several weeks or months are fine early in the process. But enough time needs to be given to investigate feasibility prior to high level scoping. Prior to implementation, time should be given to research an implementation and split the tasks out. All of this time should have been blocked in as a single unit during high level scoping.\nRequire meetings Meetings cost time and money. And in addition to the meeting itself there is time before the meeting where people ramp down, and time after the meeting when people need to ramp back up again. Both ramping phases are about 10 minutes each.\nTo monetize this for you, let’s say you have 12 employees going to a meeting, and on average they make $24 an hour, and the meeting is 15 minutes (a scrum). The total chunk of wasted time is 35 minutes or about $14 per employee. For the company that means you wasted $168. And that does not include any prep time employees had to take. In general, meetings need to be kept to a minimum and kept on track because they will end up being the single most costly event both in terms of money and productivity.\nWatch your employees work Nobody is comfortable being scrutinized. Do not set up your office so that you are looking into your employees’ cubes, and don’t wonder around aimlessly, and never under any circumstances hover. By doing this you are showing your employees that the most important thing is to look busy (a function of the keyboard and finger), not to solve problems (a function of the brain). At the point where your employees feel scrutinized they will work just hard enough to not get fired, and will partake in CYA games.\nThe best example is Office Space the movie. Little more needs to be said.","lastmodified":"2010-12-05T00:00:00-05:00","tags":null,"title":"How to Be a Bad Boss"},"/blog/20101212-deal-with-email-overload":{"content":"This isn’t strictly software related, but a lot of us have to deal with the horror that is email. Email is not a good solution to any problem, but it is ubiquitous so it is used for all things: personal correspondence, commit tracking, defect notification, task notification, etc… Email is all to often used as a mechanism to pass-the-buck.\nThe only way to deal with this email overload is to set boundaries on email usage. A lot of people will find these boundaries annoying, if not unworkable. Just stick with it and lead by example. Eventually, in a time of high stress, you will be able to get to important messages fast when other would have been will be left floundering. And at that point, folks will ignore the limits you put on email.\nMy results I am putting the summary first in hopes that you might actually try some of these suggestions. On average I get 50 to 200 work related emails a day. In any given day (saturday and sunday included) I will need to respond to 5 to 10 of them. That means that I get between 40 and 195 un-actionable emails.\nMy full process is detailed below, but basically it reduces my email load from hundreds to ones of email that I have to deal with. I also limit the amount of time I spend in email which further limits it as a point of distraction. On busy days I may never even check my inbox, instead relying on others to contact me via a different medium if they really need me. And yet, no one feels like I am unresponsive.\nUse the right messaging system to avoid email Use email for messages that do not need an immediate response. Since there is no message size limit, make sure you use the most of it. Write emails that are well detailed and specific. If at any point a message can be answered with “Ok” or some other monosyllabic word then email was not the correct choice.\nUse Instant Messages (IM,IRC,etc…) for conversations that need semi immediate responses, and a possible record. Many IM packages can log conversations for later viewing, which is useful when you forget things. There are message length and formatting restrictions so this forces the messages to be brief and specific.\nUse Phone, Skype, or face to face contact when a response is needed immediately.\nUse Twitter, or some other global message posting service to track commits, continuous integration fails, defects, etc…\nUse a wiki, or blog to track generic instructions or documentation.\nBut check email when asked Once a set of other messaging systems is up and running then folks will be able to tell you that they sent you an email. Instead of saying “I don’t check email” which can be off-putting say “oh I missed it, let me check”.\nThen check your email, read their message, and ignore all other messages.\nCheck email twice a day (only) The easiest way to train people not to use email as a crutch is not use it as one yourself. Only check email twice a day, and give a concerted effort to reduce that to once a day within a month, and once a week within 6 months. Setup the other messaging systems so that email is used only for what it needs to be. And when people step out of line, correct them.\nDon’t check email first or last Email should not be the first thing that you check in the morning, as you should not be working out of email. It should also not be the last thing you check as it will disrupt your already stressful commute/home life. Instead check email 2 hours after the start of the day, and 2 hours before the end of the day. If you work a 9-to-5 that means once at 11am and once at 3pm.\nThe day you start doing this, tell the people that send you the most email and the ones that you will most affect by the change, and no one else. You are not trying to be sneaky, but if you blast an email to everyone then you are going to make it a big deal; which it is not. The others will learn over time.\nDon’t use Inbox as an archive Once you are finished reading an email, either deal with the email and delete it or archive it. Create a separate archive folder and move emails there if you need to save them. Your goal is aways to reduce the inbox to zero\nCreate an “other inbox” By default all messages should go to your “other inbox”. Then you should have filters that filter-in messages that are important. A recommended filter might be messages where you are the direct recipient and the sender is an important sender.\nI recommend this as the first filter, but eventually your “other inbox” will become overloaded. So a standard fine grain set of filters will likely be needed to redirect messages junk away from the “other inbox”. These can just be filters before “other inbox” filter.\nUse the server’s filtering mechanism When possible have server do all the filtering that way you can use multiple email clients and will not be dependent on leaving an email client on. All Exchange servers and many online email services can do server filtering. When using Outlook, be careful, the ease of creating filters is sometimes offset by the fact that it lies about what filters can be saved to the server. Though harder, since MS didn’t spend much time on the server-side features of Exchange, it is better to build the filters on the server directly.\nReduce the inbox to zero (in one sitting) The goal is always to reduce your inbox to zero. Once all the filters are in place, the only thing left to actually deal with all the important emails (at least that is the hope). When dealing with email decide if the email should be dealt with, deferred, or archived.\nIf time is really an issue then find the messages to archive and defer first, and move them. That way what is left is just the stuff to deal with now.","lastmodified":"2010-12-12T00:00:00-05:00","tags":null,"title":"Deal With Email Overload"},"/blog/20101215-js-unit-testing-using-yui":{"content":"Ok, Selenium was a partial success that you can read about here, but it really was harder then I wanted it to be and it required a lot of setup for only a little bit of testing. I noticed that a project that I use a lot ExtJS uses a modified version of YUI Test. So I converted my Selenium test example to YUI Test.\nMy initial impression was confusion because you have to download the entire YUI 2 suite just get to get the test tools. Also, when you download the entire suite they are many different copies of the same file in various stages of minification. But following the documentation I boiled down exactly what I needed and threw away the rest. I put all the code for my test here so you can follow along.\nYou will need the following files from the YUI archive:\nbuild/logger/assets/logger.css build/logger/logger.js build/yuiloader-dom-event/yuiloader-dom-event.js build/yuitest/assets/testlogger.css build/yuitest/yuitest.js Create an HTML file that includes the css and js files: The HTML will also need to instantiate the test logger and run the test runner global. I bound the test run to a button so I could control when it ran: Now that we have a YAHOO.tool.TestRunner we need to add a test that can be run. Instantiate a new YAHOO.tool.TestCase and add it to the TestRunner. All a TestCase needs is a name and a bunch of functions that start with “test”. Everything else is automatic. Below is a simplified version of the full file. Conclusion Though Selenium is more automated that comes at the cost of being more complicated then testing should be. If I were a team of programmers then sure setting up and maintaining Selenium Remote controls would be a small part of the overall effort, but since I am not a team of programmers I think it is overkill.\nWhat I really need is an easy to run, easy to write, repeatable, unit testing framework in JS. I do the leg work on pointing my various browsers at the html and reviewing the results. When things get big enough that I need to setup a continuous integration server, or I have a QA department, then I will give Selenium another go. For now YUI test is the way to go.","lastmodified":"2010-12-15T00:00:00-05:00","tags":null,"title":"JS Unit Testing Using YUI"},"/blog/20101231-minimum-developement-enviornment":{"content":"Every developer has their own opinions on what tools are needed. This is a very malleable list, in general, but in my ten years of experience I have found some key things are are needed. Sure, you can live without a lot of these, but they will make your life easier.\nText Editor A text editor is where a programmer is going to spend most of their time. I am sure it is nice to have all the IDE features right there in the side bar or right click menu, but a lot of IDEs (Eclipse I am looking at you) add this fluff at the expense of a truly useable text editor. I don’t need my editor to take 15minutes to open if all I am going to do is edit a single file. I don’t need a plugin architecture if all I want to do is edit a script and run it from the editor.\nI use TextMate right now because it has all the features I require. If another editor comes along (one that is updated regularly) that has them then I will switch.\nGutter It isn’t enough to have a note somewhere about the current line (for example the bottom). I want a gutter that takes up constant room and shows the line number as well as other things like: cursor location, folding marks, breakpoint/quick jump points.\nSyntax Highlighting This is a must! Sorry vi. It is a very quick way of determining if the syntax is correct. It also make the code easier to read.\nWhite space highlighting Very often tabs are mixed with spaces. This is a no no, and if you are working with me I will not accept mixed code. Use one or the other, not both interchangeably. And have an editor that can show you the difference. If it is a feature that vi has then you better be damn sure your editor has it as well.\nVertical Selection There are times where vertical selection comes in handy. Basically it places the cursor as a box and anything you type is repeated once for every line. Great if you need to add indentation to the front of a lot of lines at once.\nFolding When programers write 1400 line functions (don’t scoff you know you have done it) it is nice to hide those. Basically any block that is not immediately useful to me is hidden.\nHotkeys (for everything!) The Windows method of pressing alt+X, where X is the underlined letter of the menu, is not what I mean. Everything should have a single hotkey combination what does exactly what I want. For example to run the current file as a script cmd+R seems reasonable to me. It also has to be single press. Sorry Emac ctrl+x,ctrl+r just to run a file isn’t going to cut it.\nMouse Support Yep, selection especially vertical selection is just faster with a mouse.\nQuick Search cmd+f should present a search box. cmd+g should quick search for what was already typed in the search box. And the search box should be erased when opening a new file. shift+cmd+g should search up instead of down. All search should automatically wrap without asking or prompting. I can see from the scroll bar that a wrap happened.\nSearch and Replace Search has to support regex, with matching. Replace needs to support matched items (usually $N where n is the paranes set). Replace should allow replacing in the entire file, or just in the highlighted section.\nQuick Open When opening a directory to do not all files open in the editor. I just want a file browser and a place where the text files will open. Quick open (cmd+t in TextMate) should show a dialog listing all the files that meet my typing so that I can quickly open the one I want. I do not want to have to type a full path, or search for the file. The editor should index the file names so that I can quickly open them when I want. Updating of the list of files also has to happen live.\nIndent Correction Contributors will mix tabs and spaces because they will not enable white space highlighting. If the code is already checked in and I have to deal with it then I am going to have my editor re-indent the way I want. I will commit this first then start making changes. What way my changes can actually be seen and are not hidden in indentation hell.\nScripting Support Any editor that can run scripts is an IDE. In many case they are better because you get a great editor first, and an ability to extend it. Most IDE spend a lot of time outthinking you as the user. More specifically they spend a lot of CPU and memory on things in hopes that they might be useful to you one day. This is CPU and memory that could be used by your kernel to do other things that are actually useful to you.\nCommand line tool I spend a lot of time at the command line. So I really need to be able to open a file in the current directory (or the directory itself) from there.\nControl Comments These are comments that can be placed at the top of a file to control how the editor shows the document. They are normally hidden within comment blocks so do not affect the flow of the document, but are very useful for controlling file specific changes.\nTerminal The reverse is similarly true. Sometimes you need to run a command without leaving the editor. It is best is the editor can do that via a real terminal.\nNative/Quick start/Low Memory Java apps might work on a lot of different architectures, but they are dog slow and memory hogs. The editor I use is going to be the editor I use for almost all text work. I might open a lot of files at once, or I might open only a single file at a time. It needs to be fast.\nVersion Control I use git. You will eventually use it as well so you might as well learn it.\nNon-locking Commits Distributed Network Fast Personal Branches Tags Merging SVN’s merging was a joke. It was so bad that it made you do all the work, so you basically never did it. I have yet to have serious problem with GIT merging and I use it daily. In fact I might have 2 to 3 active branches going at one time, and I will randomly merge between them. It just works. I don’t know why, nor do I care. And when it fails, it does so clearly. It doesn’t just puke random output at you.\nMisc The following more important when you start working in a group setting. At work they will often be given to you or forced upon you, but for personal projects I recommend having a set of free or OSS options to pull from.\nCloud Repository Storage This can be anything that gets the code off of your machine. It is the means of sharing code between other people and machines. The requirements are minimal, but basically it has to support the SVC tool you use (usually git), allow for both human and machine accounts, and have a permissions model to allow basic limiting of access (for example, machines get read-only access).\nWiki Some location will be needed so that you can get your ideas out of your head and into someone else’s; be it the user of your software or another maintainer (like the future you). It doesn’t have to be anything complex, and the concept of wiki style documentation is about as light weight as you can get. Have one.\nRequirements and Issue Management System / Service Once your software is used, especially if used by others, things will come up. Since no one can spend every moment of every day just implementing you will need a place to park requests so that you can focus on other things. This is where a “ticketing” system comes in.\nThe basic requirement is a system that can house multiple tickets. Each ticket needs a text box where you can describe what the issue is or what needs to be done, the ability to assign it to someone, the ability to order the ticket, the ability to comment or discuss, and finally the ability to track status (open, done, etc…).\nCaution: Many of the systems readily available were built in a time of code bloat, where the belief was that the code needed to handle all things. This has lead many of the systems out there to be a basic copy of someone else’s complex workflow; codified. This can lead to bloated and inflexible process, so unless you really need the bells and whistles I’d avoid them in favor of something really light weight. Sometimes a spreadsheet is good enough.\nTesting Framework(s) Regardless of what you are building you will need to test it to make sure your change didn’t break something that was already working. I could write entire blog entries on proper testing techniques, but the basics are the need for three types of testing system.\nUnit test - These tests - always written by programmers - test the very low level objects. Usually they focus on edge cases in the logic. Often times a programming language will have this built in. Behavioral tests - These tests - also always written by a programmer - prove that the work is complete. In other words these prove the “behavior” of the feature being coded and are often extracted from the requirement of the feature itself. The Frameworks you choose will determine the Behavioral test frameworks that you have available to you and the form those tests take. None is really better then another as long as in 2 to 5 years you can re-read the tests to determine if the described intent matches the logic. If it is difficult to marry those two things then choose a simpler test framework. Just remember DRY is not a thing in testing. System tests - These tests - written by QA - prove the software works outside of itself. That is that they test the final artifact that is produced. They are always outside the repo itself and always on the built artifact. These tests are often paired or versioned with the artifact, so that you can go back and retest old software to determine if an issue exists there or not. For a library this might be using it downstream project, so the downstream’s unit tests act as system tests for the upstreams. For a binary this might be coping the binary onto a host machine and executing it. For an appliance this might be booting it, and using its API. Notice how integration test is missing from the above? That is because it is an overly board and over used term. As such it often comes to mean “test what is easy to test”, and thus becomes an ultimate waste of time. The above testing styles have a clear intent, and intent is everything. So put on different hats and test correctly instead of what is easy.\nContinuous Integration Service / System Once the tests are written, and the compile and packaging details ironed out, there should be a system that isn’t the developer to does that. Ideally this system can be built from a minimal config using a set of directives that are defined in configuration files stored with the code itself. This ensures that the config is versioned along with the source code (i.e., Infrastructure as Code). Also, it ensure that a single system can be used for multiple projects thus dividing the maintenance costs across projects.\nWhat the CI server / service should do is:\nMonitor for change to the code If changed, it downloads the new code finds and blindly executes the IaC file reports pass / failure status Caution: It sounds simple enough, but finding the right balance of expressive IaC that runs fast enough that isn’t hard to rebuild when it is a corrupted is extremely difficult. As the build system gets more complex and more scripts are written to work around its problem the Ikea effect can kick in making the build machine seem more valuable than it really is. Don’t invest highly in an “CI ecosystem” (i.e., Jenkins), instead treat it as a set of fungible runners executing build and test scripts that are versioned with the source it builds. Be able to delete and rebuild the CI platform quickly, so that when a better CI platform come along you are in a position to switch quickly.\nArtifact Store Whatever you are building you should not use the source repo itself as the final state. Instead you should CI it (test, build, and package) the software and then capture the package somewhere else.\nWhat and how you do that will depend on what language you use, and how downstream user need to consume the artifact. In some cases your code might need to built into multiple artifacts stored separately.\nThe only real advise is that the Artifact Store has to handle the distribution of the artifacts, which means it need to be able to hold multiple versions and have a permissions model that allows write access by the build process and read-only to downstream consumers. Also, it needs to be able to handle the amount of parallel downloads of the artifacts.\nContinuous Deployment Service / System If you are building a library or artifact then you won’t need one of these. Testing, building and coping the artifact to a final storage location is going to be enough. However, if you are hosting a service then you will need to have an automated means to make the new software available customers automatically.\nThere are many styles of deployment and no one tool that is good at them all. Also, the CD platform you can use will be determined by how you run your service.\nBasically, it needs to be able to:\nCheck for the need to deploy Usually either checking for new versions of artifacts, or via manual push button. Anything else can rolled up into one of these two options. Delta the current to expected state and determine the correct commands to run Gone are the days when you write a script to deploy. Modern CD is declarative. Recover from issues in the deployment Again, declarative. You say how something can recover on error and CD figure out if it is needed and what the actual steps would be. Could be a retry, a roll back, a halt, or something else. Notify a human Caution: CD can be “implemented” using a CI platform but that is fraught with problems. It is tantamount to using a hammer on screws. Yes you can, but should you? No you shouldn’t. Pick a tool that is aimed at the end goal of the deployment and makes part easy.","lastmodified":"2010-12-31T00:00:00-05:00","tags":null,"title":"Minimum Developement Enviornment"},"/blog/20110104-what-the-restaurant-industry-can-teach-the-software-industry":{"content":"I really like “Gordon Ramsey’s Kitchen Nightmares.” Not the American version because it is mostly drama. The British version is where the substance is. The basic idea is that Gordon has 7 days to turn a failing restaurant into a successful one. Also, four to six weeks later he returns to see how many of his ideas stuck and how the business is going.\nUniversally, if the restaurant follows his advise they do well. If they fight him they fail. His tactic in every episode are almost identical and I have taken them and applied them to the software industry. Nothing new here, just interesting how good ideas transcend industries.\nKnow your customers A lot of restaurant fail because try to be what they want and not what the customers want. Your location dictates what types of food you can serve, and even what decorations should be used in the restaurant. The chef/owner has to bring the concept, but that concept has to fit the area.\nThe same is true in software. Google Wave is a good example of what not to do. There was no real problem to solve, and there was really no audience. It was a novel idea, but it lacked the understanding of what it was trying to do.\nMeet the Quality Expectation There is a minimum quality that a sit-down restaurant must meet. It is entirely based on user expectation of value. One does not go into a sit-down and expect to order off of a “dollar menu.” And one does not expect to get preprocessed, pre-made, microwaved, hamburger.\nWhat the customer never sees, that Gordon really harps on, is a clean kitchen with quality fresh ingredients.\nElegance is more important then flash If the restaurant looks like “Tijuana puked up” then there is a good bet where all the money went and it isn’t the food. A similar thing can be said about software. If the UI looks like an icon library puked on it then all the money went into adding features no one uses.\nThere is an understated elegance to that indicates a care to details. And that makes things more enjoyable.\nSimple is Key In Gordon’s words “simple is key.” This is just part of the reason that Twitter, and FourSquare are popular given that Facebook has both of these features. There’s is the simpler solution so when that is all you want that is where you go.\nPassion and Direction are key Any kitchen that does not have a passionate chef is doomed. The same has to be true of your software engineers. When the passion is lost then everything falls apart.\nThe other important part is direction. Passion without direction is bull in a china shop; great energy, but likely to cause more harm then good.","lastmodified":"2011-01-04T00:00:00-05:00","tags":null,"title":"What the Restaurant Industry Can Teach the Software Industry"},"/blog/20110110-problems-with-agile-implementation":{"content":"I really like agile programming. It keeps me close to the action, and makes me have to think about my next moves. It also keeps me informed as to what is going on around me. But in my many years of using agile I realize that, though the process itself is very nice, its implementations can tend not to be.\nProblem don’t arise from agile itself, but who and how it was implemented. If the implementer’s goals do not match the Agile Manifesto there is little chance of success.\nI have used scrum many times and the most common problems I see are:\nAgile as Micromanagement There are many reasons that folks micromanage, but usually it comes from a place of fear. This fear then leads to an over involvement, which in turn leads to the Agile methods becoming a weapon for them to alleviate that fear by remaining in control.\nIt looks like:\nHaving to break task down into hourly segments of work Having to break task that logically have to be done by a single person Having to account for ALL time taken, even time not related to code like attending meetings Having to be overly detailed in comments on work items Using deadlines instead of priority Agile tenet misused:\nIndividuals and interactions over process and tools Working software over comprehensive documentation This happens when a manager (chicken role) is the Scrum Master or when the Product Owner has say over implementation specifics. It is a confusion of roles, which in turn leads to a confusion of goals, which in turn leads lost productivity.\nAgile as a Whip Where micromanagement affects entire teams whips are an individualized weapon. Agile tools can be used to single out individuals just as well if not better than entire teams. The following are signs that it being to whip and individual.\nIt looks like:\nFiltering a burn-down on an individual basis Placing “realistic efforts” on tickets because the individual “cannot estimate correctly” Associating points with people (publicly) Associating number of tasks done with effort Associating points with hours, in an attempt to estimate a person’s working day Basically anything where measured output is more important then people Agile tenet misused:\nIndividuals and interactions over process and tools Anytime you associate numbers with people you have created a crab mentality. Their focus will stop being on software, but on making their numbers better. Those that are better at number games will succeed, those that are better at software will fail.\nAnytime you put your people under undo pressure then simple mistakes are made. This is going to later erode confidence in the team. It is going to happen like this: “you missed a comma in a Javascript file which causes it not to work in IE. That was such a simple mistake to have tested for that I am not sure you are testing any of your code.” The problem was caused by 4 hours of sleep in 72 hours of coding at the end of an over-extended sprint. The programmer was nearly delirious. It is shocking it was the only mistake, not that it was a simple mistake!\nUnfortunately I have seen this situation start innocent enough, with comments like “we don’t want to over work the staff” or “we want to make sure they always have something to do” or “we want them working on the correct things”. If the “we” in question is management then there is probably already a misalignment, and Agile is going to be used as whip to solve the problems created by the bad implementation.\nAgile as Demotivation And now we come full circle to have to do when Agile has been used poorly and management is stuck. Clearly it isn’t Agile’s fault cause Agile is perfect right?\nWhat happens here is that management is try to convince folks that it is their fault. The following are some actual quotes I have been told over my career. I paraphrased to remove details about the person and companies involved.\nIt looks like:\n“You said it would take 3 days. It took 10 days. You need to make up the difference out of your own time” It did actually only took 3 effort days, just took 10 calendar days due to distractions. “We cannot slip these date, and you have already pared back the release 2 sprints ago. You need to put in extra effort” 2 Sprints ago the direction changed and 2 sprints of effort were exchanged with 2 others without discussion. It took 1 sprint to cleanup / hide the feature that were in-progress but that wasn’t accounted for. There were still 2 sprints of effort left at this point “Agile is about being agile. Even though we are mid sprint we are radically changing direction, we are not canceling the sprint or doing sprint planning. We are just swapping out some tasks for others. We already did the estimates for you so you can keep working.” Yes, this was what was actually said to us those 2 sprints ago. Agile tenet misused:\nIndividuals and interactions over process and tools Responding to change over following a plan These are all excuses I have heard. Each time given by a person in a manager role because they are ignoring changes in the field (military term). Every choice has a set of outcomes: some good, some bad. The attempt with agile is not to mitigate bad outcomes, but to allow those outcomes to contribute to the overall direction.\nSometimes the bad outcome will be that something took too long, or that one language/tool was not the correct choice given the problem set. If for every problem that happens the developer has to take their own time, or face embarrassment, to solve the problem then they will stop making choices. Not just choices that might have bad outcomes, but choices altogether. At which point someone in a management will start making detrimental choices.","lastmodified":"2011-01-10T00:00:00-05:00","tags":null,"title":"Problems With Agile Implementation"},"/blog/20110711-understand-capistrano-without-rails":{"content":"I am working on a rails project and that rails project is distributed over several nodes in a cluster. However, each of those nodes is a standalone unit and the rails app is a small administrative frontend for that box only. For this reason the standard Capistrano deployment tasks will not work. So some hacking is in order.\nLooking around I noticed a number of projects that were just deployment steps for other frameworks and stacks, but still required Capistrano. This got me thinking that maybe Capistrano is more then just a set of deployment steps. The wiki does not make this clear but there are three parts to Capistrano:\nan engine to replicate (transactional) commands on any number of servers a rake like DSL for defining tasks to perform on remote servers, and a set of deployment recipes specifically tied to rails. The first two items are what I need and by not including the default recipes I can do anything I want. Below is my understanding of how Capistrano works.\nSample config/deploy.rb file The below code is an example that will be reference throughout.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 role :servers, 'host1' role :client, 'host2', 'host3' role :client, 'host4', :special => true task :install, :roles => :servers do run \"uname -a\" end task :test, :roles => :client do sudo 'echo TEST' end test :special, :roles => :client, :only => {:special => true} do sudo \"echo SPECIAL\" end Understanding Roles Capistrano uses “roles” to list servers with similar behaviors. The default recipe uses roles like “web” and “db” and makes it seem like that they are in some way special. They really are not. The only way they are used is by the “roles” option when defining a task.\nWhen a task defines “roles” then it will only be called on the server that have that role (see sample). Additionally options can be added to the end of roles that act like flags that can later be used by tasks to filter. Armed with understanding we can easily create arbitrary roles for use in our deployment steps.\nUnderstanding Tasks Capistrano is a simplified rake like syntax. If you are familiar with rake there are few gotcha:\ntasks cannot take input, and tasks and namespaces cannot overlap. Both of these can be worked around however. Deployment tasks should never (NEVER) take user input. User input is a variable that cannot be repeated automatically. So armed with the knowledge that if you “need” user input in deployment then you are doing something wrong rework the system to not require user input and move on.\nThere is only one special task name that I have found “:default”. If you make a task “:default” then it will simply become the task that is run when the namespace is called. It also, takes itself out of the help list so there is no task <namespace>:default.\nUnderstand Flags Task provide the options “except” and “only” that work on role flags. In the sample there is a task :special that will only run for hosts with the “client” role that also have the :special flag set to true. The included deployment recipe adds :primary to the :db role to indicate that it is the only server on which migrations should be run. I currently have no use for flags, but it is nice to understand their behavior.\nCalling other tasks In rake it is possible to say that a task depends on other tasks. In Capistrano it is not. This is not a problem since task is a wrapper around a method definition. Basically you can call a task in the current namespace by using its name like a method. And you can call methods in higher namespaces using “top”.\n1 2 3 4 5 6 7 8 9 10 11 12 13 task :upload do puts \"real upload\" end namespace :child do task :upload do puts \"child upload\" end task :upload1 do top.upload end end 1 2 3 4 5 6 7 8 $ cap upload real upload $ cap child:upload child upload $ cap child:upload1 real upload Understanding Connections In addition to being a DSL to define deployment tasks Capistrano is also an SSH connection manager. Using the “run” or “sudo” function Capistrano will execute that string at a shell on each remote machine in turn. The gotcha is that each call to “run” or “sudo” opens an entirely new connection. Therefore if several things need to be done at once use ; or && to chain commands in a single shell.\n1 2 3 4 task :test do run \"export TEST=foo && echo $TEST\" run \"echo $TEST\" end 1 2 3 4 5 6 7 8 9 10 11 $ with servers !test ** [out :: host1] foo ** [out :: host1] $ with client !test ** [out :: host2] foo ** [out :: host3] foo ** [out :: host4] foo ** [out :: host2] ** [out :: host3] ** [out :: host4] In the above example the ENV variable “TEST” is set to foo, but since the second “run” gets a new shell the ENV is reset. Also, in the second example it is clear that each “run” is executed on every server before it moves onto the next server.","lastmodified":"2011-07-11T00:00:00-04:00","tags":null,"title":"Understand Capistrano Without Rails"},"/blog/20110908-software-hazard-pay":{"content":"The military has a concept of “hazard pay” which is extra money because you are placing your life on the line. Software has something similar, though certainly not as permanent, where you are putting your socioeconomic well being on the line.\nThe following is a list of things that I think are software hazards that should require more pay.\nDress-code Policy A policy is an explicit rule created to cover an anomaly. And when the anomaly is repaired the policy remains. Dress-code policies (“dress atire shall be no less then business casual”) like social-conduct policies (“you shall report yourself in a professional manner”) are specifically vague, as to leave management leeway so that enforcement is discretionary.\nI am against these type of policys because they setup a confrontational attitude between management and workers. I personally do not know of any professional (sorry, but waiters and waitresses don’t count) that has been fired because of the enforcement of a dress-code policy, so I have to question the merits of explicitly stating one.\nLook at truly innovative technology companies and you will notice one thing lacking: a dress-code policy.\nSoftware IT Software engineers, that are good, are tinkerers. We have broken more then a few computers in our day, and almost always figure out why and fix it. The job of an IT department is to prevent employees from tinkering with computers, because it costs the company valuable effort.\nThese two departments can only co-exist as peers. In general if IT provides API to standard services like Email then software developers will never have to bother IT with questions. After all software developers are usually overqualified IT.\nIf the company codes for the Linux platform, but software developers are required to use windows machines, because that is what IT can support, this is a sign of an imbalance between Software and IT.\nNo SVC Every programmer that has been out of college for more then a semester knows about Source Versioning Control systems. There isn’t even a debate anymore, they are essential for programming. A company without at least some type of SVC is scary.\nIf you know and understand Git then you can solve this problem for the company, but if the other engineers are not familiar with such system there is going to be strong, and often emotional, opposition. Not something a professional should have to deal with.\nCommercial SVC There is no commercial SVC software out there that has the usefulness that Git does. There are a lot that fill in for the lack of usefulness by adding bloat, like IP protection. Commercial SVCs are always sold to detached CTOs by sales guys that hide its destructive nature.\nOnly a detached CTO would buy software without first letting the developers review it for usefulness. I do honestly think commercial SVC systems are destructive because they always designed for an audience that is not software developers. At best this means that the developers adapt, but usually it means that adding wildly complicated procedures to deal with the lack of a good flow.\nVSS is a great example of this failure. It only supports file locking, because it is easiest to implement. It also could not deal with merge conflicts, so external tools has to be purchased to deal with that. Also, if files were locked the nightly internal backup could not run or it would create a corrupt backup. So you couldn’t keep files locked overnight. It didn’t support branches (as we know them today) so everyone worked on the same set of files, and locked each other out all the time. It was hell!\nNo CI This is the first thing I do before any code is written. I create an empty repo, put an a rake/make/cake file that simply has one step “ci” and attach that to a CI server. As soon as we know the details of the language, testing framework, coverage metrics, etc… I adjust “make ci” to do those things.\nIt is so simple to setup that if it isn’t there then it erodes my confidence in the entire project. Tests are the only reliable communicator of assumptions, and not having a system that actively enforces those assumptions will cause greater complications later.\nRequired IDE Detached management love IDE’s, because they speak in a language that managers understand: GUI. Sometime they are useful and sometimes they are not. And most of the time they bloated, slow, and expensive.\nAny editor that can shell out is an IDE. So if using a specific IDE is required then someone is forcing their bad ideas on you. The emacs vs. vi debate still rages because they are as powerful (in cases more so) then what we think of as IDEs (xcode, eclipse, etc…), but they both are and neither requires a specific “project” file to manage a project.\nCowboys and “Heroes” Cowboy coders prefer to work alone, often thinking themselves and their code superior to other developers. If there are not code reviews, it is usually because of adamant opposition from these guys. Even if they lose the code review fight they will fight be exempt from code reviews themselves.\nAll they do is hack some stuff together without thought of quality or maintainability and call it done. They are dangerous, and the quality of the system will suffer as the project gets too complicated to fit entirely within their own heads.\nA “hero” is a very loud cowboy. They are the software equivalent of “yes” men. They tell management what they want to hear, and often gain position greater then their experience and expertise. Unfortunately they almost always spell doom to a project in one way or another. Sometimes it is outright fail to produce a workable product, and sometimes it is a mass exodus of skilled workers.\nBoth cowboys and “heroes” have a habit of getting entrenched and are very hard to unseat, but the project will not survive them.\nCoding Silos For reason beyond my understanding managers usually frown upon agile approaches like pair programming. It is seen as wasting company time and tantamount to theft. Instead they would prefer programmers to “just do their job” which involves writing code and not much else.\nA vertical silo is where you are expected to do everything from the GUI to the database layer. On small projects this is expected and even wanted. But rarely (unless you doing it on your own time) will a progressional project be that small. When a person holds the entire stack in their heads then the apparent usefulness of MVC (or similar patterns) breaks down. This later leads to scaling problems.\nA horizontal silo is when there are many projects for the company and you maintain the same role on ALL of them. A major problem is in solving the wrong problems. For example: you are the DBA for all apps in the company. You have a very expensive Oracle DB and know it well, and have a bunch of homegrown management scripts. Therefore all web apps run on the same Oracle DB server because it is quicker and easier for you, and now there are two single points of failure (you and DB). Sqlite would have been good enough, but that isn’t how specialized people think.\nWhen all you have a hammer all problems look like nails - Barry Gruenberg.\nAnother problem with horizontal silos is a stagnating. For example: a junior engineer wants to use a new distributed key/value store like Cassandra or CouchDB. You argue with management that a simple key/value table in the already sharded Oracle DB would be good enough and would save the company money in both hardware and effort. You might be right, or you might be wrong; but you are guaranteed not be to exposed to a different way of thinking. In the long run that simple act adds complacency and stagnation into large companies.\nThe only sustainable solution is a cross functional team, where everyone has areas of strength and weakness but they work on the system as a whole.\nValue Based Estimates At market driven company it is not uncommon to see estimates done by those not qualified to do them (management). A marketing department will usually attach a proposed value of a given feature, or product. This automatically skews the estimate by placing an upper bound (the value minus require profit).\nIf the company communicates well then realistic estimates are given by developers, and if those estimates are higher then the projected value then the feature or product is not invested in. In marketing driven the realistic estimate are not done or are ignored and the project moves forward with the value estimate. This then adds instant legacy as the correct implementation was not worth the effort, so a half-assed job was done instead.\nHopefully, the team at least invested in a means to fix past mistakes. But sometimes that isn’t a feature worth the effort.\nDetached Management Much of what has already been stated come from the same source: detached management. A detached manager will often blame the developers for failures in software and procedures he/she is forcing on them. “A carpenter doesn’t blame his tools” is a common excuse. If a developer is lazy and has no interest in keeping up with the industry it is a valid excuse, but more often the manager just doesn’t want to hear that they did not fully think through the problem before forcing a solution.\nEarly in the game this looks like a manager that doesn’t show up to scrum calls because they are too busy.\nOverstaffing One of the easiest ways to make a budget look needed is to hire a bunch of staff. If there is an influx of new hires, especially if they appear unqualified or under qualified then it is possibly for budgetary reasons.\nThe unfortunate side effect of overstaffing (even if the staff is qualified) is laziness. More staff equals more cooks in the same amount of space, which in turn equals more confusion and less efficiency. This lack of efficiency leads to an imbalance of responsibilities and therefore some will be overworked and other underworked. Underworked employees become complacent, and overworked ones become annoyed and will work less. This drives overall productivity down.\nAdding staff at any stage of a project makes the project take longer and that will continue to be true until we learn to download and upload expertise.\nMarket Driven or Marketing Driven company Companies driven by a Marketing Department (however good) are reactive companies. There is no vision of how things should be, or why they should be that way. It takes a visionary to reduce a companies offering, and instead focus on quality. Marketing departments do not do this, they always add more.\nIn fact, there is a certain critical mass in which a marketing department exists to exist. This is the point at which the marketing budget becomes as big (or bigger) then the R&D and Engineering departments, so they can convince users of the usefulness of the product (which probably isn’t).\n“Putting out fires” Anytime I hear “putting out fires” I know the project is doomed. Things probably got this way gradually over time, but at this point there is too much technical debt to warrant moving forward. Projects that cannot move forward die.\nThe only way to prevent this situation is to head it off early by dealing with heroes before they cause trouble and keeping marketing in check.\n“Continuous Work” Anytime I hear someone from a company (usually marketing) mention continuous or unending work I get nervous. I understand that it is an attempt to make it seem like the job is on going and therefore safe, but I am not a factory worker. The stuff I work on should have a natural conclusion, and the knowledge I gained should then be used on another project.\n“Competitive Salary” Anytime I hear “Competitive Salary” I know three things:\nit is a lie, and by extension the company lies. they gear to recruiters, not people being hired they under value employees and will undervalue you Huge red flags are raised.\nAgile Lip-service A lot of companies are flocking to Agile as an end all be all. But that is exactly what Agile is not. I have detailed in Problems with Agile Implementation a number of bad implementation and companies that think Agile is a silver bullet are likely to make some or all of them.","lastmodified":"2011-09-08T00:00:00-04:00","tags":null,"title":"Software Hazard Pay"},"/blog/20120309-factory-girl-automatic-tests":{"content":"Early in a project I started to use factory girl without fully understanding it. After many months of creating steps like Given /^(\\d+) blog exists$/ and Given /^the following blogs exist:$/ I started to come up with generic functions that would build those steps.\nStupid me for not checking that factory girl already does something like that. All you have to do is include factory girl’s step_definition file:\n1 requie 'factory_girl/step_definitions' Once you start using FactoryGirl correctly there are a world of new features that can make your steps both cleaner and more concise. Here are some tips I have found via trial and error.\nTip 1: Do not reinvent the wheel Factory girl will create steps for all factories that you have register.\n1 2 3 4 5 6 7 8 class Blog < ActiveRecord::Base end FactoryGirl.define do factory :blog do name 'Foo' end end 1 2 3 4 5 6 7 8 Scenario: Showing example steps Given the following blogs exist: | name | | first blog | Given a blog exists with a name of \"first blog\" Given a blog exists Given 41 blogs exist Given 14 blogs exist with a description of \"Test\" Tip 2: Use association, Do not add more steps then are needed Factory girlassociation are automatically created before the factory is created and they are automatically linked. It only supports the belongs_to behavior, so keep that in mind.\nUsing the “Given the following XXX exist” step you can define attributes, on the theassociation, in the table. If we leave the association out then a default is created. If we define an attribute then it will be found or created using that attribute.\nLets say you have a product and it can belog to a category. You do not need to create a category.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Category < ActiveRecord::Base end class Product < ActiveRecord::Base belongs_to :category end FactoryGirl.define do factory :category do name 'Foo' end factory :product do association :category name 'Bar' end end 1 2 3 4 5 6 7 8 Scenario: Bad Given a category exists with a name of \"Foo\" Given a product exists with a category of \"Foo\" Scenario: Good Given the following products exist: | name | category | | Foo | name: Bar | Tip 3: Attaching files via CarrierWave Since cucumber is a text file it doesn’t make much sense for you to define full files in steps. It also doesn’t really make sense to embed full file paths in the tests. Instead, you can use a Transient attribute and some code so that in cucumber you define a file name and in the factory it converts to an actual file.\n1 2 3 4 5 6 7 8 9 10 11 12 class Product < ActiveRecord::Base mount_uploader :file, FileUploader end FactoryGirl.define do factory :product do ignore do file_name 'small_image.png' end file { File.open(Rails.root.join(\"path/to/files\",file_name)) unless file_name.blank? } end end 1 2 3 4 5 6 7 Scenario: Products Given a product exists # 1 product with an included file whose file_name was \"small_image.png\" Given the following products exist: | name | file name | | No image | | | Big image | big_image.png | Tip 4: Fixing a circular dependency between two models Lets say you have a Store model and User model. And a User can both work at and own a Store. If you put associations in both the User and the Store model then each will try to create the other, infinitely. We can reuse the transient method as before to break the circle.\nThe trick is to avoid defining an association in both factories, but instead use a transient attribute in one factory to simulate the behavior of an association. Also, since transient attributes are not likely to have the same level of sophistication as the associations you should use the association to define the more complex of the two models.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class User < ActiveRecord::Base belongs_to :store has_many :stores, :foreign_key => 'owner_id' end class Store < ActiveRecord::Base belongs_to :owner, :class_name => 'User' end FactoryGirl.define do factory :user do name 'John Doe' ignore do store_name nil end store {Store.find_or_create_by_name(store_name || 'Test store')} end factory :store do name 'Test store' association :owner, :factory => :user end end 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Scenario: 2 users named 'John Doe', and 2 stores named \"Test store\" Given a user exists Given a store exists Scenario: 2 users both working at the same store Given the following users exist: | name | store name | | John Doe | Test store | | Jane Doe | Test store | Scenario: 2 stores both owned by the same person Given the following stores exist: | name | owner | | MD Store | name: John Doe | | CA Store | name: John Doe | Tip 5: Fixing a circular dependency between the same model Let say you have a Category model, and that model can belong to another Category (a tree) then you cannot use an association or you get the same infinite recursion issue as before. Here we can use a transient attribute along with an after_create hook to simulate the behavior we want.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Category < ActiveRecord::Base acts_as_nested_set end FactoryGirl.define do factory :category do name 'Foo' ignore do parent nil end after_create do |category,attributes| unless attributes.parent.blank? parent = Category.find_by_name(attributes.parent) category.move_to_child_of(parent) end end end end 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Scenario: Has no parents Given the following categories exist: | name | | Foo | | Bar | | Baz | Scenario: Nested tree Given the following categories exist: | name | parent | | Foo | | | Bar | Foo | | Baz | Bar | # + Foo # |+ Bar # |- Baz ","lastmodified":"2012-03-09T00:00:00-05:00","tags":null,"title":"Factory Girl Automatic Tests"},"/blog/20120519-the-everyone-elses-job-is-easy-paradox":{"content":"The “Everyone else’s job is easy” is a easy trap to fall into and an almost impossible trap to get out of. My point is best illustrated using the stereotypical Employee/IT relationship.\nThe stereotype from the Employee’s perspective is this “I hate dealing with IT. They are a bunch of asses, and never do things right, and my computer is alway worse after they leave then it was with the problem that I called them for. What is their problem? It is their f’n job to keep these computers working so that I can do my job. I have no interest in working this weekend because IT was not able to get my computer fixed in a reasonable amount of time. When ever I walk by their desk they are always goofing off, if they only did their job I could do mine, how hard could it be.”\nThe stereotype from the IT’s perspective is this “I have to fix everyone’s stupid computer. If only they would read that Wiki article I emailed about the proper way to do this thing then I wouldn’t have to keep fixing things. I tried to prevent these kind of issues by not giving admin rights to users, but they always need to install some piece of crap software, and they won’t take the time to learn to take responsibility. After all with great power comes great responsibility, and I see none of it. In addition to keeping everyone’s computer working all the damn time I also have to keep all the internal and external services running. Besides most of the time these people are on the phone chit-chatting, or standing around the water cooler; clearly their job easy enough for a monkey, so why should I rush.”\nThe problem It was a pretty extreme example, but I bet that everyone has acted this way to another individual at least one. How could you not, after all when tentions are high and emotions are frayed devaluation of other is a natural defense. And though it is a natural defense it is certainly not an acceptable one.\nI am certainly no Psychologist, but here are some ways I have found to prevent the escalation.\n1. Understand how hard everyone’s job actually is In term if difficultly, both mentally and physically here are the jobs in order from most difficult to least.\nBering Straight Fishermen Any other Fishermen Logger Political leader/Dictator Fugitive Mangers (above you in the company) Everyone else’s Job Your job Yep, once you understand that more then likely the person that you are dealing with has a more difficult job then you do then it is easier to compromise.\n2. Humanize This is hands down the hardest thing to do when nerves are frayed, but remember the other person in the argument is also a human.\n3. Eliminate Cognitive load Cognitive load is simply an amount of mental work that must be processed before learning can be accomplished. For example, it is not uncommon for requests between departments to have to be done in a formal way, like though a task tracking system. This is completely unacceptable.\nIf communication between departments has to be done in a formal way then the department HAS to consider every other depart as a customer. Would you as a customer of Comcast think that it is acceptable to have to login into a Comcast support portal in order to request them to fix your TV? No, you would call them, or email them, or do something else that is convient to you, and your would let Comcast deal with formalizing the request in a way useful to them.\nThe same has to be true between departments. If you are in IT and you have to formally use Autotask, or Zendesk, or anything else then YOU MUST also find a way to get the requests into that system. Other departments have to be able to call, email, or walk over to YOU and make requests.\nBut at the same time, other departments are as busy as you, so you have to remember that once asked turnaround can take a while.\n4. Detail the difficulty If the other person doesn’t know a lot about all your responsibilities then they are more likely to thing of things as easy. And most of the time everything that is being asked of you is easy, because it is your job and you are good at your job; right?\nThe problem is usually more likely the amount of stuff you have to do before you can do this one other “easy” thing. At one company I worked for we liked to call these type of situation marketing fire drills. Marketing would have a client that promised to buy 200 hundred widgets every year if only the widget has some feature. But we have 20 other features that have to be implemented before we can start on the new feature is started.\nSo in this case the difficulty is prioritizing all the “easy” features. Especially since each market person was responsible for their own projects and did not have a company wide view of things.\n5. Be concise, but be human Often times people see conciseness as talking about work and nothing else. But in my experience more time is wasted talking about work topics then for any other reason. I don’t mean that work topics are a waste of time, but how many time have there been several hours of talk when 15 minutes of actual investigation would have eliminated the need for talk.\nWhat I mean about being concise is only add information if it is relevant. “Because it is always done this way” is not relevant, no matter what you might think. So unless you have specific experience keep it to yourself. Also, raise any relevant questions, and take any relevant advice.\nBy eliminating the work conversation waste you can and should spend time talking about personal things. I have always been a huge fan of eating lunch away from the office with co-workers. It is the best to respect people and be respected. And by humanizing, barriers can be eliminated.","lastmodified":"2012-05-19T00:00:00-04:00","tags":null,"title":"The \"Everyone Else's Job Is Easy\" Paradox"},"/blog/20120520-the-problem-with-best-practices":{"content":"I hate the term “Best Practices” for two important reasons. First, in an attempt to be concise the eliminate the most import information: the reasoning. They are often just the call-to-action statement. and they are often passed down as policy. Some best practices are good practices (for example pre-flight checklist) when applied to the correct situation. But without the reasoning statement, it is hard to tell if the practice can be applied to other situations. If I had to apply a pre-flight checklist before starting to code then I would waste a huge amount of time.\nSecond, best practices eliminate critical thinking. With a misnomer like “Best Practices” it is not surprising that they are not questions. And in most situation questioning a “Best Practice” is taken as an affront to the person/department that introduced the Best Practice. I am not sure why this is, because very rarely is a Best Practice the best practice for every situation. But since they defy questioning once instituted they become technical dept when they outlive their usefulness.\nInstead of “Best Practices” I prefer “Pro Tips.” But the name itself is not as important as to how it is presented. In order for me to consider a “Pro Tip” it must have three parts: a list of benefits, a list of drawbacks, and description of behavior.\nA best practice: Always backup your work using git I do not think that any software engineer is going to argue that backing up work is a good thing. But by phasing it this way the problem because that “git” becomes an afterthought and therefore an annoyance.\nA Pro Tip: Use git as a journal Using git as a backup mechanism is a good thing. But it is better to use git to journal what you are doing. So instead of work for an entire day, committing everything at a single time, and then pushing everything at once you should do a single complete thing, and commit that. Usually commits happend every 5 to 10 minutes, but they are generally self contained.\nBenefits:\nYour work is backed up You and others can see your train of thought You can return of any point and attempt other options You eliminate the fear of trying new things Focused work Drawbacks:\nYou have to know git to make the most of it You have to take the time to make smaller commits You have to push more often You might have to start branching in order to keep organized Difficult for messy thinkers (tinkerers) See: a pro tip is a bit longer, and more detailed, but at the same time it is more convincing. The reader can then choose when and how to try the tip, and also is free to adapt the tip to their given situation. For example: a developer is going to use git differently then a designer; but git could be useful for both.","lastmodified":"2012-05-20T00:00:00-04:00","tags":null,"title":"The Problem With Best Practices"},"/blog/20120911-testing-rails-in-ie-through-pow":{"content":"My Problem I can’t test in IE (but the client wants it to work in IE 7 and IE 8). So I have to fool things.\nNote\nPow is now defunct. Services like nip.io, or sslip.io can be used instead. Just replace *.myhost.dev with *.nip.io and it will work as expected.\nMy Setup A mac - because I need it to work the first time every time Rails RVM - because you should be using it PostgreSQL - it is better then MySQL and actually easier to setup after you do it once. Pow - because it is small, spins up the rails server when you need it and the url looks like http://myhost.dev, not http://127.0.0.1:3000 (some browsers will attempt to look up “localhost” via google search, before asking DNS) TDD Cucumber - because it is easier to read integration level testing Rspec - because I like it (it both backs cucumber, and also us the unit testing for models and libraries) Capybara Capybara-webkit - so the browser is opened headless and doesn’t intrupt other work My Solution First do everything as normal, since you will be developing faster and only at the END worry about IE. This is counter to a lot of thinking, but I have found that if you stick to good TDD and are testing using an actual opened browser then the only issue you will end up with are CSS related issues.\nBefore you start Since it is likely that you will only have CSS issues it is a requirement that you make the main CSS work on HTML5 compliant browsers. And add exceptions for the others.\n<%= stylesheet_link_tag 'real' %> <!--[if IE]> <%= stylesheet_link_tag 'ie' %> <![endif]--> <!--[if IE 8]> <%= stylesheet_link_tag 'ie8' %> <![endif]--> <!--[if IE 7]> <%= stylesheet_link_tag 'ie7' %> <![endif]--> Use a Windows VM Go to https://github.com/xdissent/ievms and install the various IE VMs. I have not had luck with IE6 and IE7, but IE8 works fine.\n1 curl -s https://raw.github.com/xdissent/ievms/master/ievms.sh | IEVMS_VERSIONS=\"8\" bash If it fails, just keep rerunning it, or try a different version\nBoot and wait for driver detection On first boot I usually go in as Admin and let windows detect everything and install all the needed components. This isn’t a genuine version of windows so after 30 days you will be locked out, at which point you just need to revert to the “clean” snapshot.\nInstall the OS extensions For some reason the CD doesn’t autoplay for me. Just enter d: in the Start Menu -> Search and then select the installer.\nAdd Fake DNS Pow munges DNS on the Mac so that you don’t have to use “localhost”. I actually really like this about Pow and so will remain using it. The only thing that you need to do is edit the windows Hosts file and add the Fake DNS there as well.\nLogin as the IEUser Start -> Search -> “Notepad” Right click and select “Open as administrator” File -> Open Change URL to “C:\\WINDOWS\\system32\\drivers\\etc\\hosts” Add the following (change the host names as you need) “10.0.2.2 myhost.dev” “10.0.2.2 myhost1.dev” Now open http://myhost.dev in IE and it will work ","lastmodified":"2025-08-09T00:00:00-04:00","tags":null,"title":"Testing Rails in IE Through Pow"},"/blog/20140410-code-does-rust":{"content":"Fourteen years ago Joel Spolsky wrote an article entitled “Netscape Goes Bonkers”. In that article he states that “old software doesn’t rust”. The rest of the article is good, but that statement is “off”.\nTo be clear, as a direct comparison software contains no metal to oxidize and therefore cannot actually “rust”. But as an analogy, over time untouched software will slowly degrade and eventually stop working. So the corrected statement should have been “[untouched] old software rust[s].”\nRecently, some login code I wrote a few years ago magically stopped working on FireFox. This functionality continues to work on all other major browsers. However, due to the fact that FireFox decided to change how it handled cookies my software is now slightly less capable of performing as expected.\nAs a result, instead of making further progress on a new feature I am forced to a take a moment to clean and fix the rust spot. Not a challenging fix and not indicative of a fundamental design problem, but rather, an annoying, little issue which needs to be addressed. Of course, the big picture view is that, over the years, I’ve had to deal with hundreds of problems just like this one. Meaning that when I don’t closely maintain a project’s codebase and adapt it to dependency updates, then it’s performance and functionality is diminished. Very similarly to how an unmaintained metal surface rusts.\nThis leads me to believe that software does in fact rust.\nThe solution? I’ve found that there is no substitute for taking the proactive approach and resolving these problems early on because eventually the rust will become too significant and leads to the software being scrapped altogether.\nImportant\nIdeas age like fine wine, but software rusts.\nHow to slow the problem There are different ways to solve the problem depending on the situation.\nUse some one else’s Stable Interface Any time there is an interface between two systems there is corrosion. This is as true in software as it is in the real world. Tires exist because road surfaces chew-up (corrode) anything that slides across them. The tire is the car’s interface to the road. It is designed to be easily replaced when it wears out. The same method works for software.\nIf the interface between two systems is highly corrosive (constantly changing) then the best interface is someone else’s. For example, Heroku is a going to be a better interface to “cloud” hosting then Amazon. Amazon is infrastructure in the cloud; basically the road. Heroku is web hosting in the cloud and uses Amazon as a base; basically the tire. So if all you want to spend your time on is building the car, then use Heroku as the tires.\nUse a standards base API If the interface between two systems is only slightly corrosive then add a standardized “socket”, to protect yourself. Car’s don’t produce enough point-heat to light a cigarette, and instead of piping the 800+ degree exhaust into the cabin or giving unprotected access to the car’s battery the car manufacturer introduced a socket that could power a heating coil. The socket protects the car, and provides a standard interface. And by being standardized, anyone (not just the manufacturer) can create an adaptor to fit the socket.\nAn Application Programming Interface (API) is the software equivalent of a standardized socket. Any place your system needs to be accessed, simply create an API, even if you control both ends. Now your tests can focus on ensuring an unchanging API, to catch any wear that needs to be addressed.\nIgnore it until it fails If the interface is non-corrosive then test for wear. Many systems “guarantee backwards compatibility” (at least until they it breaks the first time). This is the software equivalent of a well lubricated non-corrosive interface. It is still not immune to corrosion, but you don’t (and shouldn’t) actively protect yourself. Instead, add some once-in-while checks. Cars usually get a 50K mile service to check for these low wear areas. Do something similar with your software.\nOf the millions of cars that get a 50K mile service a small percentage will have a catastrophic failure, where one of those non-corrosive interfaces corroded. The same can happen in your software, but the cost of constantly checking those parts is far greater than any saving gained by not letting it fail. It is better to follow good practices (like modular design, and not cutting corners) than it is to search for failures everywhere all the time.\nFail Fast… If the interface is solid and shouldn’t fail, then simply fail to launch. For example: Cars need engines; that is a solid interface and a hard requirement, without it you go no where. And if an engine dies while it is running there is the expectation that the car will not be able to propel itself anymore. We might be surprised that an engine breaks, but we are not surprised when a broken engine stops a car. Web servers often need databases and network connections. So code to bind to a port or connect to the database should allow the app to fully fail.\n…With a retry Because software and hardware fails so much that real software has to start assuming the previous exit did so uncleanly so it has to start by checking for and performing cleanup. This is the major reason that a full retry is often the best solution.\nAlso, because retries are so common they are usually someone else’s Stable Interface that can be used.\nAside: Degrading Software And now I hear you saying “Woah, apps should degrade gracefully.” Honestly, they shouldn’t, at least not self-degrade. You would never drive a car, get a flat, and expect the car to change its own tire. No, you pull over, install the spare tire, and at a degraded level drive slowly to a tire shop to have it fixed (at least that is what you should do). But the car did not degrade itself. You, as the driver, are expected to make that choice for the car.\nThe same is true in software. The software should not degrade itself. There should be a watchdog for your software which periodically checks to see if it is alive. If during one of the checks the software is found dead then it should be resurrected. If it suffers SIDS then the watchdog notifies someone, otherwise it is business as usual. To be a good-citizen, your software should play nicely with the watchdog\nThe Half-dead problem Permanently-half-dead is the common state in degrading software where your software has one or more forms of degradation and as a result, it cannot self-correct. It will never die because it is degraded, but it can never perform fully either.\nThis is the worst possible state of software because the issue - by definition - is unclear. The software is running, or appears to be, but isn’t doing what it is supposed to. This is not a testable state, so there is no amount of QA that can be performed to prove the issue is resolved. This is not a place you want your software to be able to get into, so don’t.\nAside: Defensive Software As a further aside, people often confused degrading software with defensive software. But to clarify, defensive software is only concerned with preventing bugs due to unforeseen usage. Things like swapping direct memory manipulation with a memory manager, and code reviews, and testing are defensive. Defensive software can and does terminate before it can degrade into doing something foolish. Take for example:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class DegradedUser def name=(name) # If the user provides too much data, # ignore their wishes, do what we want, # and don't tell them. # # This is degraded, and bad @name = name.to_s[0..16] end end class DefensiveUser def name=(name) # If the provides too much data, # tell them immediately and fail to continue # # This is defensive, and good (though annoying to the user) raise 'name is too long' if name.length > 16 @name = name end end ","lastmodified":"2014-04-10T00:00:00-04:00","tags":null,"title":"Code Does Rust"},"/blog/20140622-google-sheets-query-language":{"content":"A while back, my wife and I started keeping a budget. We need something very easy that shows us where we are every moment. Also, to ensure that it is not something we “forget” it must be something that we manually enter.\nI created a Google Form in order to allow us to capture the receipts. The form dumps into a Google Spreadsheet. I then use a Pivot Table and the Google Query Language to create a Chart. In this post I will cover the entire process.\nStep 1: Create a form Create a google form Add the following fields “Company” - text- required “Date” - date - required (don’t include time) “Amount” - text - required (add a “number” validation) “Category” - list - required. We added the following Other / Unknown Baby Supplies Car Entertainment Gas Groceries Home Medical Pet Supplies Restaurants / Fast Food “Comment” - paragraph Choose response destination Choose a “New Spreadsheet” Send the form to yourself and anyone else that needs to enter receipts At this point you have a Form which submits to a Spreadsheet. I recommend bookmarking the link in your smart-phone so that it is easy and quick to add receipts right after your purchases.\nStep 2: Pivot Form usually records into a sheet called “Form Responses” which I assume here.\nSelect “Form Responses” Select Data -> Pivot table report… Rows - Add “Category” Values - Add “Amount” Now you should have a two column table. On the left are the categories. On the right is the sum of all the values of that category.\nStep 3: Google Query In order to chart the budget vs the actual spending we need to create another table.\nInsert a sheet named Budget. Label the columns: Category, Budget, Actual, Query Copy all the categories to column A Add the budgeted amount to column B Add the following to column C =if(isna(C2), 0, C2) Add the following query to column D =QUERY('Pivot Table'!A:B, \"select B where A='\"&A2&\"'\",\"\") #NA means that are no receipts for the category and can safely be ignored. Copy and paste cell C2 and D2 to the rest of the cells in the column Google will change the internal references (A2, and C2) to the correct cell name, so you don’t have to. The Google Query Language is defined here. It is a good read to see all the power of this language, but I am only going to explain the parts that we need.\nQuery QUERY takes three arguments: range of values, query string, and optional headers. I am going to explain them in reverse.\nThe headers are guessed if nothing is provided. This would cause the query to take two cells, which is not the behavior I wanted. By adding \"” it removes the header.\nThe query string tells google what data we are selecting into the cell. In our simple example it is a direct value select using a conditional. This is because the column order may not be the same in both sheets. “select B” means to choose the “B” from whatever rows match the query. “where A=’\"&A2&”’\" means to limit the rows returned to those where the value of cell A matches the value of A2 in this sheet. The “&” is the string concat operator.\nThe range of values tells Google what it is allowed to look at. We use the ‘Sheet’!Col:Col form in order to select data from another sheet. We only provide the columns A and B because we want to look at all rows.\nISNA Charts cannot deal with non-number columns. Since the query can produce a non-number output (#N/A) we need to add an additional level of processing.\nisna takes a cell and returns if that cell is #N/A.\nif take a boolean, a true value, and a false value. If the first argument is true then the true value is returned. If the first argument is false then the false value is returned.\nStep 4: Chart Charts can only take numbers and they can only accept contiguous cells. A, B, and C are the columns that we want to chart.\nInsert -> Chart Data range: Budget!A1:C14 Use row 1 as headers Chart type: Bar chart Add a chart title Step 5: Publish The point of this document is to know where your money is going quickly. In order to make it easy publish the document. This will make google convert the document into a HTML version which is easily viewed in your smart phone. The chart will even be convert to an image.\nFile -> Publish to the web… Check “Automatically republish when changes are made” Copy the link Send to link to anyone that needs to be kept informed about the budget ","lastmodified":"2014-06-22T00:00:00-04:00","tags":null,"title":"Google Sheets Query Language"},"/blog/20140623-rake-publish":{"content":" Note\n2025 Update. This blog is no longer Octoblog, but the content is still valid.\nOctoblog (the engine behind this blog) uses Jekyll. As such, it also supports the publish flag. I a previous post I detailed how I added this feature back to Octopress. Here I will show you a little rake task to easily publish an unpublished post.\nRequirements For a post to be published I wanted a few things to happen:\npublished: true was set in the YAML front-matter date: <todays date> was set in the YAML front-matter The file was moved to today. Here is what I came up with for my Rakefile.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 desc \"Publishes an unpublished entry by changing its name, updating its internal timestamp, and setting published: true\" task :publish, :pattern do |t, args| require 'tempfile' require 'fileutils' files = Dir[File.join(source_dir,posts_dir,args[:pattern])] raise \"No files found matching pattern\" if files.size == 0 raise \"Too many files match pattern\" if files.size > 1 name_time = Time.now.strftime('%Y-%m-%d') file_time = Time.now.strftime('%Y-%m-%d %H:%M') file = files.first name = File.basename file new_name = name.gsub(/\\\\\\\\d+-\\\\\\\\d+-\\\\\\\\d+/,name_time) w = Tempfile.new('publish') File.open file do |f| scan = false f.readlines.each do |line| # limit scanning to the YAML front matter scan = !scan if line == \"---\\\\\\\\n\" unless scan w.write line next end line.gsub!(/published.*/,\"published: true\") line.gsub!(/date.*/, \"date: #{file_time}\") w.write line end end w.close FileUtils.rm file FileUtils.mv w.path, File.join(source_dir,posts_dir,new_name) end Line 6: I take a argument and get a list of files matching the pattern Line 7 & 8: I found it was too easy to screw up a pattern and publish too few or too many posts. For that reason I am explicit about the failure. Line 15: Calculates its name for today. Line 16 & 37: Generate a temp file to write to. I found that bad things happened if you published a post that you wrote the same day. Solution was to write to a temp file and then move the temp file later. Line 19 && 22: An inelegant solution to isolating scanning to only the YAML front-matter. Line 28 & 29: Update the YAML front-matter. ","lastmodified":"2014-06-23T00:00:00-04:00","tags":null,"title":"Rake Publish"},"/blog/20140802-bats":{"content":"Shell scripting is a great tool, but rarely is it tested. Enter BATS! In this post I will give a quick tutorial on how to use it to test scripts.\n1 2 3 4 5 6 7 #!/usr/bin/env bats @test \"running a command\" { run foogrep \"bar\" foo_file [ \"$status\" -eq 1 ] [ \"$output\" = \"1: bar baz\" ] } Note\nShameless plug: Before we start, I recommend downloading the language grammar package - language-bats for the Atom editor.\nIf you have never used RSpec or other testing framework the idea is simple: your code is run against expectations and if those are met then the tests pass. The framework deals with the heavy lifting of executing the tests, printing the results, and providing to the correct interface to Continuous Integration servers.\nBATS is a test runner for Bash scripts. Before each run BATS takes the file and splits each test into its own file. BATS then runs each test file to see if passes or fails. Anything you can do in Bash you can do in BATS, and if any command fails then the entire test fails.\nA Basic test BATS syntax for a test is @test \"desc\" {}. But if you want it to run the file individually you should add the shebang line. The simplest test looks something like this:\n1 2 3 4 5 #!/usr/bin/env bats @test \"something\" { false } This isn’t very useful, but it will generate a failing test.\nSkipping tests Sometimes it is a useful to skip a test. Just add skip at the point you want to the test to be skipped. You can add a description or not.\n1 2 3 4 5 6 7 8 9 10 11 @test \"just skip\" { skip } @test \"skip for a reason\" { if [ \"$x\" == \"foo\"]; then skip \"Because of foo\" fi # more tests } Running a command Bash doesn’t let you return strings from functions, so if you are trying to capture output and status then you have to roll your own, or use run. run returns the commands output to $output, and its exit code to $status. This makes testing on output and status easier.\n1 2 3 4 5 @test \"check output and status\" { run echo_foo [ \"$status\" == \"0\" ] [ \"$output\" == \"foo\" ] } Hooks Sometimes multiple tests need to share the same state. In testing every test should stand on its own and leave no artifacts. To accomplish this we can use the setup and teardown hooks.","lastmodified":"2014-08-02T00:00:00-04:00","tags":null,"title":"BATS"},"/blog/20140917-ruby-sucks-kind-of":{"content":"Ruby sucks! Kind of!\nOk, well not really. Not even a little. But there seems to be a misconception about what ruby is. I hope to clarify somethings by first comparing it to other languages, then by ripping it apart in a constructive way.\nEvery computer language serves to let human control computers, and nothing more. Every language creator chooses an abstraction level that they feel fits with their needs. And in the end every computer language creates strings of 0s and 1s.\nHere is a high level summary of some languages that I use:\nAssembly - Turn computer instructions into easier to remember words C - A set of preprocessors on top of assembly C++ - An Object abstraction on top of C Java - An Everything is an Object attempt Javascript - Sceme for the browser Python - Its academic Go - Easy concurrency Ruby - Reads like prose Assembly For those few of you that were alive in the time before assemby, the solution was direct bit manipulation. This was done by manually flipping switches that represented bits or feeding punch cards. Assembly was a huge improvement.\nInstead of memorizing human instructions and then having to translate to and from binary one could use simplified language. The downside is that every CPU manufacturer had their own language set.\nC Given that every CPU had its own Assembly language, code could not be ported to multiple computers. Enter C, the third attempt to create a portable language. A and B came before C, but no one uses them or even references it any more.\nThe syntax and 2 pass compiler design has become the standard for most “compiled” languages.\nC++ After many years of success, someone had the bright idea that complexity can be reduced by adding objects and encapsolating details. C++ is born using the same syntax as C, but with OOP. Early version of C++ were just a preprocessor which created C code.\nAs the techniques of OOP became solidified the C++ compiler became a compiler in its own right. However, unlike C most C++ are not bootstrapped (meaning it is not written in the same language as they compile).\nJava Java was born out of Sun Mirocosystems. It was originally intended to be an embedded language. The idea being that you would install the JVM as the device’s OS and your program would remain the same no matter what hardware was running. In the end this concept was years ahead of its time, but ultimately a failure of its original goal due to bloat.\nIt was also one of the first languages that forced classical OOP with its “everything is an object” approach. Unfortunately, for performance reasons the language still contained primitives and other basic non-OOP contracts making its choice to force the “main” method to be a callback of an arbitrary object kind of ridiculous.\nAfter the embedded aproach did not pan out Sun went after the web. During this time they also split the language from the JVM, published the bytecode spec, and picked up XML as preferred configuration format. The verbosity of XML seemed to fit the verbosity of Java as a whole.\nThe Java ecosystem has had a years of turmoil due to the Oracle purchase of Sun Microsystems and not liking OSS. After years of litigation things have stabilized.\nJavascript Javascript was born in Netscape to allow HTML to be programmed. It uses prototypical inheritance instead of classical inheritance. Its language is based on C++ with concepts of several other languages. Javascript has a lot of advanced concepts which of its developer did not know how to properly utilize given it an undeserved bad name.\nA few well constructed libraries and the advent of JSLint made it clear what was good code and eliminated much of the early pains.\nℹ️ For many years I though “classical inheritance” was because it was the classic / old form (like “classical music”), which gave it air of correctness. That is actually not true at all. Classical - in this case - comes from “of or relating to class hierarchy”. Python Python was born in academia. It has infinite precision and a clean syntax. About the only ding is that the cleanlyness of the parser is more important then the expressiveness of the language. Other then its non-expressive syntax it is a first rate language.\nGo Go is a new comer to the language scene. It aims to make concurrent programming easy. It is OOP but has a C-like syntax, and is also opinionated about the syntax. So opinionated that they have a go fmt program which will rewrite files using the expected indentation and alignment.\nIt is clearly a “new” language, and lacks some fairly common features of other langagues like versioned dependancies. But it has an active community of caring people which are solving these issues.\nRuby Ruby was created to be an expressive language that was a joy to work with. Unfortunately this has meant that much of the interpreter is nearly impossible to understand. The upside is that unlike any language before it, it can act as its own DSL.\nRake, for example, is ruby’s version of Make. While Makefile is a special text that the make program parses, a Rakefile is just ruby code. Adding require 'rake' to a ruby file simply causes it to load rake.rb which add functions which make defining tasks and dependancies like writing english.\nAs promised this is the point where I list rubies weaknesses.\nRuby is too magic The ruby interpreter is not a point in time interpreter, like python or Java. It is a dynamic re-interpreter like LISP. There is no first parsing pass for calculating and caching the structural elements of the program and then a second runtime pass.\nInstead, it is a single pass, building the structure as it goes. This has the added benefit of allowing you to dynamicly add, redefine, or remove structure like classes or methods on classes. However, this can be and is very confusing for those unsure how those that structure was built.\nThis “feature” is what causes ruby to appear like magic. The solution requires a full understanding of all dependencies that are loaded.\nAny class can be changed at any time, which is scary This is just the “ruby is too magic” argument under a different name.\nYes, ActiveSupport from Rails adds a lot of nicities like 1.day.ago to ruby. Generally this more useful then not.\nIt’s nothing but bloat It is true that ruby program are larger then C programs, but they are no larger than most other interpreted languages.\nIt’s slow With the speed increases for the latest version of Ruby it is faster than both PHP and Python now. And JRuby runs at nearly C speeds. So it is fair to say that is an apples-to-apples comparison that ruby is no slower then any other interpreted language.\nThe GIL is slow and prevents concurrent programming Not really true. The Global Interpreter Lock (GIL) gets a lot of flack from those that haven’t actually looked at the code, but it is pretty smart. The GIL gets locked anytime there is IO and unlocked anytime a thread is about to sleep. For example: let’s say you have a loop and in the loop you spawn a thread and do a web request to http://google.com. The first thread would build an IO request to a socket locking the GIL, but once the request hit the wire and the thread would sleep releasing the GIL and the loop would continue.\nJust like any semaphore locking if you do too much while the GIL is locked then your app is slow. If you are smart about how you chunk your code Ruby’s GIL is actually faster and simpler then Python’s threading system.\nThey change the syntax every release There have been only a relatively few releases which actively broke an existing syntax, and in those cases it was because the original syntax was flawed.\nIt doesn’t work on Windows Use Cygwin, or a Linux VM.\nThere is no self-installer, or GUI builder Rubiest are command line junkies. There are several ways to install ruby for every platform.\nRuby doesn’t really make much sense as a GUI application. However, there are several packages that aim to make Ruby the glue language. I leave it as an exercise of the reader to find them, but while you are looking you may also want to checkout a language called lua.\nIt’s good for Rails, but not much else Ruby on Rails it the keystone framework, and the reason why most use it. However, both puppet and chef (deployment tools) are written in it.\nRails is huge and slow Yea, and getting bigger and slower every release. That is common as frameworks grow, but guess what, you don’t need all the bells and whistles. While getting bigger Rails is also getting more component-ized, allowing you to exclude the stuff you don’t need.\nConclusion I hope by this point you get the sense that no language is bad per se. Each language starts with a set of assumptions which inform a design. In turn that design defines the language, which in turns defines what is easy and what is hard. You have to pick the right tool for the job. And each language represents a single tool.","lastmodified":"2014-09-17T00:00:00-04:00","tags":null,"title":"Ruby Sucks... Kind Of"},"/blog/20140921-using-amperstand-instead-of-if":{"content":"You can use && to perform a logical if. And there are a few reasons it may be better to use &&.\nIf I was a Computer Scientist I might pull out logic maps or Turing completeness or do a mathematical proof. If I was a Computer Architect I might argue that I do not need to prove anything and you should trust my experience. Luckily I am a Software Engineer, so I will prove my point with tests.\nNote\nThe following is written in ruby, but the will work in any Turing-complete language with operator precedence. Just remember in Ruby that the last statement is returned.\nLet’s take if a then b. The entire purpose of the if then it to only execute b if a is true. If I were to write it using tests I might do it this way:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 require 'spec_helper' describe \"if then\" do class Test def a; end def b; end def if_then if a b end end end let(:t) {Test.new} it 'executes a' do expect(t).to receive(:a).and_return false t.if_then end it 'executes b if a is true' do expect(t).to receive(:a).and_return true expect(t).to receive(:b) t.if_then end it 'does not execute b if a is false' do expect(t).to receive(:a).and_return false expect(t).not_to receive(:b) t.if_then end end These tests pass. So, I will make no change to the tests. But, I will refactor the Test class to use &&:\n1 2 3 4 5 class Test def if_then a && b end end These tests also pass! And you are probably thinking that I duped you somehow. Let me explain why this works.\nLogical and (&&) and logical or (||) can both be short circuited; meaning that if a certain condition is met they can immediately return a value without needing to execute more statements. For and if any value is false then the entire statement is false. So the first time the program sees a false value it can return. For or the first true causes true to be returned.\nThe actual execution for a && b is as follows:\nExecute a if a is false return false if a is true Execute b If you look carefully that is identical to the execution path of if a then b, which is why all the previous tests pass without modification.\nif not then Just like && maps to if then, || maps to if not then or in some lanugages like ruby unless then. I leave it as an exercise for the reader to write the tests, but the code is as follows:\n1 2 3 4 5 class Test def if_not_then a || b end end if then else else is just the if not case. Since, || is eqivilant to if not we can chain it after &&.\n1 2 3 4 5 6 class Test def if_then_else # if a then b else c a && b || c end end Why is this useful? For some reason many languages can execute && and || a lot faster then if then else, but I very rarely consider performance a good excuse for crappy looking code. I have some simple reasons to use && instead of if:\n1. You are chaining actions If the things that you are chaining are actions being performed and those action return whether they were successful then it often reads better as &&.\n1 2 3 4 5 # bad paint_it_black if find_a_door # good find_a_door && paint_it_black 2. You are likely to chain additional items Once you nest logic then readability goes out the window. Using && helps.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # bad if column_a_is_a_string if column_b_is_a_number if not column_c_is_a_boolean raise 'Bad' end end end # good column_a_is_a_string && column_b_is_a_number && column_c_is_a_boolean || raise 'Bad' 3. You don’t know how many items you need to chain Sometimes you need to parse a file of conditionals, or will be given a list of conditionals. Most of the time you cannot ensure that there are only two items on the list, so the if then contract is not useful, but a variants of && and || will work.","lastmodified":"2014-09-21T00:00:00-04:00","tags":null,"title":"Using `&&` Instead of `if`"},"/blog/20140929-golang-stream-file":{"content":"Go (golang) is a highly concurrent language. But more then that it is a simple language built using modular components and strings them together in useful ways. This modularity has lead me to play around a bit and one of things that I found was a easy way to stream a file.\nAt first I was very unhappy that Go did not support the default compression provided by the xz tool. At work, we use xz to stream compress very large amounts of data. But since go had no native support I thought that it would be a lost cause.\nTurns out there is no native support because there doesn’t have to be. Below is the basic code.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 func xzReader(r io.Reader) io.ReadCloser { rpipe, wpipe := io.Pipe() cmd := exec.Command(\"xz\", \"--decompress\", \"--stdout\") cmd.Stdin = r cmd.Stdout = wpipe go func() { err := cmd.Run() wpipe.CloseWithError(err) }() return rpipe } Declare the function to take an io.Reader as STDIN and return io.ReadCloser as STDOUT Create a pipe so I that can capture the program’s STDOUT and broadcast it as a readable stream Register xz as the command to execute Set the command’s STDIN Capture the command’s STDOUT Run a command in the background Run the command until there is an error Close the pipe when the command exists Return the read end of the command’s STDOUT At this point you have a io.ReaderCloser (which is an io.Reader). You can use it anywhere you would use an io.Reader. For example lets say you have a compressed CSV file. The following code would open the file, decompress it, parse it as CSV, and print that to the screen:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func main() { fp, err := os.Open(os.Args[1]) if err != nil { log.Fatal(err) } defer fp.Close() xz := xzReader(fp) defer xz.Close() csv := csv.NewReader(xz) for { line, err := csv.Read() if len(line) > 0 { println(line) } if err != nil { break } } } Define the main function Open a file Test for errors and bail if there are any Close the file pointer after the program exits Pass the file as the stdin to the xz command and capture its output Close the command’s STDOUT after the program exits Pass the decompressed file to the CSV parser and get back a csv.Reader Loop until the stream is closed Read a line Print the line if it exists Note: in Go, it is perfectly reasonable for a command to work and fail in the same call. If I check for an error before printing the line then I may miss the last line of the file. Check for a stream error and break the loop It is a very common paradigm in Go to take an io.Reader and pass it to a function just to get back a new io.Reader, and take that and pass it around and get back what you want (like a string or CSV row). Once you get your head around the fact that Go’s power is that everything is a small well defined lego block, and that they all fit together, then it stops being strange that you would take one kind of interface and exchange it for exactly the same kind of interface.","lastmodified":"2014-09-29T00:00:00-04:00","tags":null,"title":"Golang Stream File"},"/blog/20141017-go-concurrency-patterns":{"content":"One of Golang’s strengths is its composability. This strength is only useful if you know how to make those composable parts. That is where patterns are useful.\nGolang is concurrent, which is not necessarily parallel. However, to make things concurrent you have to break thing into atomic steps. If you are careful in how two step share information then you can easily turn concurrent design into parallel design. Go channels make this communication stupid simple, and thus make concurrent design very easy.\nIn this post I am going to share what I think are the basis of most other concurrency patterns: The Generator, The Worker, and The Consumer.\nA note about parallel vs concurrent Before we start, it might be useful to separate parallel and concurrent. Parallel is doing multiple things (usually the same or similar things) at the same time. Concurrent is doing multiple disparate things to converge on the same point. In other words, the purpose of concurrency to arrive at a solution, while parallel allows us to repeated arrive at a solution in the same span of time.\nAnother way to think of it is commuting to work, vs working. We all drive to work in parallel. However, at work our work is concurrently producing company value.\nSo why is this useful? OSes since the early days of Unix were parallel; they were routing phone calls after all, which is just a lot of the same thing. This in-turn meant that the OS level abstractions (threads and processes) were parallel and no thought was given to concurrent abstractions.\nThis in-turn was exposed directly by most programming languages. However, just as driving to work doesn’t necessarily make you useful these constructs on their own isn’t useful. Additionally, there are a lot of edge cases and paradoxes when parallel code tries to become concurrent.\nHowever, properly concurrent code can become parallel code quite easily. Go luckily allows us to structure our code concurrently and then handles the parallelism without you having to use a thread.\nBelow are the basic types of concurrent actions. If you keep your code bitesized and focused then parallelism is automatic.\nThe Generator A generator simply does work and places that work on a channel. What that means is really up to what is needed but the pattern is:\nCreate a channel Create closure which does work Execute the closure as a goroutine, closing the channel when done Return the channel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func generateInt() chan int32 { // 1. Create a channel out := make(chan int32) // 3. Spawn a closure go func(){ defer close(out) // 2. Do work for i := int32(0); i < 100; i++ { out <- i } }() // 4. Return the channel return out } The Worker A worker takes stuff off an input channel, works on it, and places the result on an output channel.\n1 2 3 4 5 6 7 8 9 10 11 12 13 func enlargeInt(in chan int32) chan int32 { out := make(chan int32) go func() { defer close(out) for x := range(in) { out <- x*2 } }() return out } The Consumer A consumer takes stuff off an input channel and consumes it. There are two primary way to do this: Blocker and Signaller.\nBlocker The blocker form simply run the code. This version is useful if there is a main loop which shouldn’t exit until all work is complete.\n1 2 3 4 5 func printInt(in chan int32) { for x := range(in) { fmt.Println(x) } } Here is a full working version.\nSignaller A signaller is actually a varient of the Worker pattern, where the output channel is used to signal the completion of work.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func printInt(in chan int32) chan bool { out := make(chan bool) go func() { defer close(out) // Work until the channel is closed for x := range(in) { fmt.Println(x) } // Single that I am done out <- true }() return out } Here is a full working version.","lastmodified":"2014-10-17T00:00:00-04:00","tags":null,"title":"Go Concurrency Patterns"},"/blog/20141215-emacs-key-binding":{"content":"I recently switched to Emacs as my editor of choice. It has taken a bit of work to get it to where I like it. My full settings are on github here.\nIn this post I will share how I added a key binding to only a single mode.\nA key binding is just maps a key sequence to a lisp function. A global key binding can be added in the following way:\n1 (define-key global-map (kbd \"C-/\") 'comment-or-uncomment-region) This causes a key sequence to be added to a map. The global-map contains the global key bindings regardless of mode. This is only useful if the command makes sense in all modes. In a lot of cases keys should only be bound to a mode.\nEach mode has its own map which emacs only uses when you are in that mode. This is how you add mode specific key bindings. The map for any mode is the mode name with “-map” added. You will need to have the mode loaded for the map to exist.\nThe following will add Agenda mode to OrgMode, which is off my default.","lastmodified":"2014-12-15T00:00:00-05:00","tags":null,"title":"Emacs Key Binding"},"/blog/20150210-emacs-full-screen":{"content":"On a Mac the short cut to put a window into full-screen mode is ctrl + cmd + f. Unfortunately this does not work directly for emacs. Here I will explain how I made it work.\nℹ️ This is only applicable to windowed versions of Emacs! First, I needed to figure out if it was possible to make emacs full screen. To do this I turned to the help system for a function M+x fullscreen<tab> did not turn up anything useful. Then I checked the apropos help command. C+h a fullscreen produced the following:\nType RET on an entry to view its full documentation. toggle-frame-fullscreen <f11> Toggle fullscreen mode of the selected frame. That looks promising so I run it as an interact command: M+x toggle-frame-fullscreen and it does what I expect. Now to bind it to a key.\nI added the following to my config:\n1 (global-set-key (kbd \"C-s f\") 'toggle-frame-fullscreen) ℹ️ “s” is “super” which is what the “cmd” on a Mac maps to. I run C+x e to evaluate the lisp code and then I use the keyboard combination ctrl + cmd + f, but nothing happens. What gives?\nI use the help system describe-key to find out what it is bound to C+h k <ret> ctrl + cmd + f prints \" is undefined.\"\nAs it turns out “ctrl + cmd” will almost always present as a numerical key value. Using the help system causes emacs to print what it sees when those buttons are pressed. It is simple matter of using angle bracket key form (like is printed). (global-set-key (kbd \"<C-s-268632070\">) 'toggle-frame-fullscreen) works like a champ!","lastmodified":"2015-02-10T00:00:00-05:00","tags":null,"title":"Emacs Full Screen"},"/blog/20150210-emacs-tabs-and-tab-groups":{"content":"From other editors I am used to having Tabbars. Switching to Emacs I miss that behavior. Emacs does have a tabbar plugin, but it isn’t quite what I want.\nBy default it groups the tabs in a seemingly random way. I am sure it makes sense if you wrote it, but for me I want the things group by my projects. For me a project is a directory which has a .git directory at its root.\nFirst I created a simple function to get the project root, or return nil.\n1 2 3 4 (defun my-project-root () \"Return the root of the project.\" (locate-dominating-file default-directory \".git\")) Basically this search all parent directories until it finds one containing the .git directory. The built-in function details are. locate-dominating-file BASE FILE searches the parent directory tree from BASE until it finds FILE. default-directory is a file local variable which is the directory of the current file. Next I set the tabbar-buffer-groups-function to return the group name of the current file. The only requirement is that the function return a list, but it is recommended that the list only contain a single item.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 (setq tabbar-buffer-groups-function (lambda () (let ((dir (expand-file-name default-directory))) (cond ((member (buffer-name) '(\"*Completions*\" \"*scratch*\" \"*Messages*\" \"*Ediff Registry*\")) (list \"#misc\")) ;; All Magit status goes the same place ((string-match \"^*magit\" (buffer-name)) (list \"#magic\")) ((string-match \"^COMMIT_EDITMSG\" (buffer-name)) (list \"#magic\")) ;; All Cider windows ((string-match \"^*nrepl-server\" (buffer-name)) (list \"#cider\")) ((string-match \"^*cider\" (buffer-name)) (list \"#cider\")) ;; Group tabs based on project root ((my-project-root) (list (my-project-root))) ;; Use the current dir (t (list dir)))))) Here we capture the absolute path of the current file. Then we check a bunch of things to determine which best represents the group name. The first thing we do is group various special buffers together. Then we use the project root to group files. And if nothing else we use the file’s directory as the group name.\nlambda ARG BLOCK creates an anonymous function. let VAR BLOCK sets a variable and then calls a block. The variable is then local to that block. cond TUPLE TUPLE... executes tuples until the first time in the tuple is true. When it finds a true tuple it executes the second item and returns it. member ITEM LIST returns true if the first item is contained within the second list. buffer-name returns the name of the current buffer. list ITEM converts an item into a single item list. string-match REGEXP STRING returns the index of the regex in the string, or nil. my-project-root See above. ","lastmodified":"2015-02-10T00:00:00-05:00","tags":null,"title":"Emacs Tabs & Tab Groups"},"/blog/20151230-hugo-blog-development":{"content":"Hugo does a great job of separating out configuration, content, themes, and local overrides. Each getting their own file or directory. But it provides no deployment scripts.\nFor comparison, Octopress/Jekyll leaves it as an exercise for the developer to separate configuration, content, themes, and local overrides, but it provides a deployment script.\nUsing GitHub pages and a little bit of git wizardry and the deployment process is pretty easy.\nConfiguration Before you start you run hugo new site -f yaml command to create all the files and directories Hugo uses. I prefer YAML to TOML or JSON, but Hugo supports all three.\nI used the following as my basic configuration.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # config.yaml --- # Configure the basic behavior of URLs baseurl: \"<http://jkamenik.github.io/>\" canonifyurls: true # Name the site title: \"Random Software Inklings\" # Whenever new files are written, use this format metaDataFormat: \"yaml\" # The format shown here is the same one Jekyll/Octopress uses by default. permalinks: post: \"/blog/:year/:month/:day/:title/\" Themes Hugo has a lot of themes to choose from in the Showcase. But it is easy enough to build your own theme if you want; I went with the Icarus theme.\nIf you want to try a couple different themes, just download them to the ./themes diretory and use hugo -t theme-name to generate the site with that theme.\nWhen it came time to pick my final theme, I decided to use git submodule so that I could upgrade the theme in the future without having to deal with merge conflicts. git submodule works by marking a sub-directory as separate git repository. There are some gotchas so read the entire explanation on the git-scm site.\n1 2 3 4 # Make sure you repo is clean first!!!! $ git submodule add git@github.com:digitalcraftsman/hugo-icarus-theme.git themes/hugo-icarus-theme $ git add . $ git commit -m \"Adding theme\" If you need to download a new version of the blog just be sure to init the submodule.\n1 $ git submodule update --init --recursive Theme Overrides If there is anything about a theme that you do not like it can be overridden. For example, I wanted Gravatar to used as my profile pick. I did this by copying ./themes/hugo-icarus-theme/layouts/partials/profile.html into ./layouts/partials/profile.html I then changed the image link to use gravatar.\n1 2 3 4 5 6 <aside id=\"profile\"> <div class=\"inner profile-inner\"> <div class=\"base-info profile-block\"> {{ with .Site.Params.gravatarHash }}<img id=\"avatar\" src=\"<https://www.gravatar.com/avatar/{{.}>}?s=200\">{{ end }} <!-- <img id=\"avatar\" src=\"{{ .Site.BaseURL }}css/images/avatar.png\"> --> ... Rest is unchanged ... You will notice that I pull gravatarHash from Site.Params. This needs to be added to the config.yaml file.\n1 2 params: gravatarHash: \"md5 checksum of your email address\" DeploymentThe hugo site has a very good tutorial on using github pages here. I used a modified version with my blog.\nThe blog will reside at http://jkamenik.github.io. This means the repo MUST be named jkamenik.github.io, and the static site MUST be on the master branch. If you are using Hugo for project documentation then the setup is a bit different and you should read the tutorial linked above.\nKnowing that Hugo generates the static site in ./public the easiest thing to do is use git subtree to track a directory as a ref. git subtree is similar to git submodule in that they both aim to make a directory of one repository a ref to another repository. The difference is that git subtree does this as a merge strategy, while git submodule does it by maintaining two separate full repos.\nIn my case I will be developing the blog on the draft branch, and publishing it on the master branch. For this reason git submodule is not a good fit, but git subtree is ideal.\nHere is the setup.\n1 2 3 4 5 6 7 8 9 10 11 12 $ git checkout --orphan master $ git ls-files | xargs git rm --cached -f $ ls | xargs rm -rf # remove all but the hidden .git directory $ git checkout drafts README.md $ git add README.md $ git commit -m \"Initial Commit\" $ git push origin master $ git checkout drafts $ rm -rf public $ git subtree add --prefix=public origin master --squash $ git push origin drafts $ git subtree push --prefix=public origin master It seems more complicated then it is. Basically, I just create an orphan branch (one not associated with the commit history of the current branch), and then I load that branch in the ./public directory of the branch I will be maintaining. That way using a single git commit will take care of both repos at the same time, and GitHub will be happy because master will have the static site.\nDeployment becomes:\nAdd and commit changes Regen site Push drafts branch Push master subtree (./public) The following script does that taking a commit message or supplying a default.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #!/bin/bash echo -e \"\\\\\\\\033[0;32mDeploying updates to GitHub...\\\\\\\\033[0m\" # Build the project. hugo # Add changes to git. git add -A # Commit changes. msg=\"rebuilding site `date`\" if [ $# -eq 1 ] then msg=\"$1\" fi git commit -m \"$msg\" # Push source and build repos. git push origin drafts git subtree push --prefix=public origin master Hugo does a great job of separating out configuration, content, themes, and local overrides. Each getting their own file or directory. But it provides no deployment scripts.\nFor comparison, Octopress/Jekyll leaves it as an exercise for the developer to separate configuration, content, themes, and local overrides, but it provides a deployment script.\nUsing GitHub pages and a little bit of git wizardry and the deployment process is pretty easy.\nHugo does a great job of separating out configuration, content, themes, and local overrides. Each getting their own file or directory. But it provides no deployment scripts.\nFor comparison, Octopress/Jekyll leaves it as an exercise for the developer to separate configuration, content, themes, and local overrides, but it provides a deployment script.\nUsing GitHub pages and a little bit of git wizardry and the deployment process is pretty easy.\nHugo does a great job of separating out configuration, content, themes, and local overrides. Each getting their own file or directory. But it provides no deployment scripts.\nFor comparison, Octopress/Jekyll leaves it as an exercise for the developer to separate configuration, content, themes, and local overrides, but it provides a deployment script.\nUsing GitHub pages and a little bit of git wizardry and the deployment process is pretty easy.\nHugo does a great job of separating out configuration, content, themes, and local overrides. Each getting their own file or directory. But it provides no deployment scripts.\nFor comparison, Octopress/Jekyll leaves it as an exercise for the developer to separate configuration, content, themes, and local overrides, but it provides a deployment script.\nUsing GitHub pages and a little bit of git wizardry and the deployment process is pretty easy.\nHugo does a great job of separating out configuration, content, themes, and local overrides. Each getting their own file or directory. But it provides no deployment scripts.\nFor comparison, Octopress/Jekyll leaves it as an exercise for the developer to separate configuration, content, themes, and local overrides, but it provides a deployment script.\nUsing GitHub pages and a little bit of git wizardry and the deployment process is pretty easy.","lastmodified":"2015-12-30T00:00:00-05:00","tags":null,"title":"Hugo Blog Development"},"/blog/20160101-forcing-factors":{"content":"A forcing factor (a.k.a Forcing Function, for us nerds) is any factor that forces you to make a choice. They are often thought of as bad because when you are forced to make a choice that choice is not likely going to be a good one.\n“Check” in the game of chess is one such negative forcing factor. Global climate change is another. However, they can be used for good if you take control of them.\nIf you think about forcing factors as the start of a feed back loop then you can use that to your advantage. The following are some ways I used them to make my life better.\nBroccoli Once upon a time - like most kids - I hated broccoli. So I forced myself to eat it first instead of last. By forcing myself to eat it first I creating a forcing factor that prevented me from eating the food I liked until I finished the broccoli.\nAs an adult I actually like broccoli, so this particular forcing factor isn’t necessary, but I still use the idea when trying new foods. Instead of resisting, I simply try new foods first, and if I don’t like it then I finish it anyway before moving to the foods I like. That way my doggy bag contains the food I do like and I get a second meal.\nCleaning Up After the Dogs I dislike cleaning up dog poop. But I really dislike when people don’t clean up after their dogs, so I always clean up after my my dogs when I walk them. And I use that dichotomy as a forcing factor.\nI used to let the dog out into our fenced in backyard to do their business I would simply let them out and back in. This meant that if we wanted to let the kids play back there (they’re toddlers) I would have to take 30 minutes and comb the backyard for the poop bombs, or clean it off the kids when started playing in it. It was not pleasant.\nI would have to do the same before I mowed the backyard as well. Or I risked stepping in poop, or worse having it explode out of the lawn mower. Yuck!!!!\nWhen I walk the dogs I always carry poop bags and clean up after my dogs. So now, I simply leash up the dogs and walk them around the front yard with poop bags two to three times a day. When the dogs go I clean it up and trash it. Now I never have to worry about there being poop bombs in my backyard.\nAt first it was annoying but now it is habit, and sometimes if the mood strikes me I simply take the dogs on a full walk around the neighborhood. Its better for me and the dogs.\nYard Work I hate mowing the lawn, but I like a clean yawn. Specifically I like my front yard to look good for my neighbors. So, I use this to force me to do the entire yard.\nMy wife would prefer I do the “important” stuff first, so that I could quit in the middle. But half completed work bothers me.\nI do the edging first (because I won’t do it if I wait). Then I mow the backyard, then the front, and finally I blow off the sidewalk, driveway, and patio so that everything looks nice. By doing it that order it ensures that I do it all, so when head in for the day I am not worried about how much work I have left.\nConclusion Forcing factors can be bad, but you can still use them as a force for good in your own life. Take a step back, figure out what makes you avoid something and do that thing first (eating yucky food). Or find a thing you really don’t like and find a way to make it habit (picking up dog poop). Or find a way to organize a daunting tasks which ensures you complete it (yard work).\nOne benefit - not listed above - is that as you find and use forcing factors on yourself you become more aware of them. It becomes easier to see when others are using them for good or ill; which means your response will naturally become more calculated and less forced.","lastmodified":"2016-01-01T00:00:00-05:00","tags":null,"title":"Forcing Factors"},"/blog/20160118-story-points-done-wrong":{"content":"Ever said or heard something like:\n“How many hours per Point?” “How many days is a 3 point story?” “Why can’t we just use hours?” If so…\nTo be honest Story Points are probably the trickiest part of Scrum largely because everyone “wants” them to be something that they are not. But once you understand them they are far more useful and accurate then any other form of estimation (that I have seen).\nStory Points are Complexity not Time Story Point do not aim to be an accurate calendar representation of work. And because of this, when used correctly they are. Story Points are a measure of risk and complexity combined into a single number, nothing more and nothing less.\nAt most you should use 5 story point values, but ideally you have fewer. The scale you choose doesn’t matter just so long as everyone knows that there is no direct relationship. For example, 2 smalls don’t make a medium.\nPoints are chosen by comparing the unestimated story to a known golden story whose complexity is well understood. Finding the golden story is the hardest part of using Story Points, but also the most important. Usually it is best to pick a single generic medium story. That way the comparision is simply “is this story more effort, less effort, or similar effort to the golden story”.\nGrooming: Never do Points at Planning I won’t go over what or how to do Grooming, but by the time planning starts it is already too late for Story Points to have value. Planning is the time where a story is tasked out and (if the teams so chooses) hours assigned to tasks. If you wait, then Points will be equated to hours and the value of Points is lost.\nVoting Each team member should vote in silence and at the same time so they are not influenced by a dominate person (usually a PO or senior dev which under values complexity, effort, or risk). The idea is to get a general consensus, not a unanimous verdicts. If there is an event split or one extreme outlier, then a discussion should be had so that everyone understands. A new vote should be taken after the discussion. Repeat until everyone is close to the same number then pick the point value of the majority and move on.\nI Need a Time! - Velocity By abstracting Points from Time you are no longer conflating the two. However, projects are done based on calendar time, so points need to be translate. Enter Velocity.\nVelocity is simply the average number of Story Points the current team has done in the last several sprints. It doesn’t pretend to have perfect knowledge. It is just a calculated number which accurately represents how the team produces.\nOn the small (at the sprint level) velocity has little to no accuracy, but on the large (at the release level) it is very accurate. Think of it like a radioactive half-life. At the atom level you have 0% chance of guessing when it will decay into the stable form. However, at a larger quantity you know exactly how many atoms will decay in the time period (just not which ones).\nIf you are used to moving warm bodies around, or individually assigning stories, then velocity may seem like a step backwards. And maybe you (or a manager) have even seen short term gains doing your own thing, but eventually things will turn sour (usually at the worst possible time). Go read Mythical Man Month, and return here when finished.\nA stable velocity is a fragile thing, and it doesn’t pretend like it isn’t, but if you are willing to protect the team (and by extension the velocity) then it is amazingly accurate. For example, if over the last 3 sprints the team has averaged 10 points a sprint and you double the team size and put 20 points of work into the sprint then there is no hope of success because the velocity number is still 10 point / sprint.\nVelocity changes slowly, so don’t fall into the trap of playing with team makeup in an attempt to gain velocity. It is always better to increase throughput (amount of new feature) while leaving velocity alone.\nThroughput Throughput is simply the number of new features added. It is related to velocity, but not the same. In fact, whenever someone says “we need to increase our velocity” this is actually what they mean. The easiest way to increase throughput is to limit risk in one of the following ways.\nCut scope Does “user login” imply a “forgot password” process? If so, split it into two explicit stories. Cut stories horizontally Stories are often cut vertically, which usually leads to interesting half done features. Consider cutting stories horizontally first, then vertically. To ensure that stories aren’t half done and need to be revisited. Release Earlier - with an easy upgrade path A Min Viable Product is likely 2 to 3x more features then are actually needed. In many cases it is better to 1/2 the feature count and 2x the quality and release early. Remember, once you release you must support; which is going to chew up time, so get used to it. Don’t focus only on the “customer” A “user” is any user of the system including Devs (developers and QA). Making their jobs easier will increase throughput overall. So make sure stories reduce tech debt, and increase automation and testing. We overcommit A very common problem, if you use Story Points at sprint planning is to overcommit the team. Like previously said, points have no accuracy in the small. So using them as a measure of the sprint will result in no accuracy.\nPoints may (and should be used) by the PO to help order the backlog so an even mix of large and small tasks make it into a sprint. If the sprint is mostly large stories (which are large because of risk or complexity) then the sprint is likely to fail.\nThe correct way to do planning is to take each story from the backlog and task it out. For each task add an estimated time to complete, and don’t forget about QA or outside team tasks, but refrain from assigning tasks. Keep tasking stories until you get to the story that can only be partially completed during the sprint. The PO may decide to accept a half done story, or decide to rearrange the backlog to find a smaller story.\nOnce the stories are pulled into the sprint it is entirely up to the team to decide when and in what order to do the stories. Ideally, the entire team would bring a single story to completion before starting another, but there are cases where it doen’t work completely. For those cases the devs should move on to testing.\nWhen deciding how many tasks will fit in a sprint a good rule of thumb is 5 productive hours per day per person. This is because email, team discussions, and scrum meetings all take away from development time. The larger the team it more coordination is needed therefore 5 hours is only true for teams of 3 to 5 devs. For larger teams take away 45min of productive time per additional dev.","lastmodified":"2016-01-18T00:00:00-05:00","tags":null,"title":"Story Points Done Wrong"},"/blog/20160202-gitflow-simple":{"content":"Gitflow is a great workflow to ensure you maintain constant ever increasing version numbers with enough room to fix mistakes. The downside is the slowness of deploying new features. GitFlowSimple is a simplified version which can be expanded to standard GitFlow when needed, but is less effort when deploying new features.\nAssumptions This method assumes the following are true:\nYou understand the following features of git How to branch from a non-HEAD commit How to deal with a merge conflict That linear history is an illusion (so you use git log -a --graph). You understand what is meant by a “SNAPSHOT” version. Basically, ever changing code which devs can use, but never goes into production. You have read and understand GitFlow You need to release changes quickly. For example, you are maintaining several projects and need to add features to a library package; GitFlowSimple is ideal for the library. You don’t care about contiguous version numbers. they The numbers are ever increasing, but may not be in sequence (a use might get 1.2.5 as the update to 1.1.0). This branching style is ideal for library code that is not normally seen by an end user, or end-user software that is automatically updated since the end-user doesn’t usually keep track of version numbers. You mileage may vary, and you can always migrate to the standard GitFlow workflow when needed.\nBranching Where GitFlow has “develop”, “master”, feature, release, and hotfix branches, GitFlowSimple only has “master”, and feature branches (or you can call them release branches if you like merging several features into a release). Everything that is done on the extra GitFlow branches is be done directly on a feature branch or master branch in GitFlowSimple.\nThe main branch “master” is the only main branch and (just like GitFlow) it must remain production ready. And just like GitFlow this is the branch where version tags are created.\nUnlike GitFlow this is also the branch where version numbers are bumped. The flow looks a little bit like this:\nHEAD is version 1.0 (which is also tagged) A feature branch is created from the HEAD commit of master The feature is bumped into a snapshot image as follows: The version file (inside the source code) is updated to 1.1.0-SNAPSHOT The change is committed, immediately The change and the feature branch are pushed to the central repo The feature is developed The feature is tested and merged (git merge --no-ff) On master, the version is bumped as follows: The Version file is updated to 1.1.0 (no “-SNAPSHOT”) The change is committed The change is tagged All changes are pushed to the central repo Merging using --no-ff prevents git from linear-izing the commits and makes it easy (using git log -a --graph) to see all the branches/features.\nSupporting Branches The hotfix and feature branches are both used in GitFlowSimple. The two branches serve the same purpose as they do in GitFlow. The release branch serves no function in GitFlowSimple.\nAn example GitFlowSimple is best shown using an example. Lets assume the project is currently at 1.0, and a defect that popped up needs to be fixed, and at the same time two new features need to be added. I am going to deal with each in turn, but in actuality they will be done in parallel.\nHotfix A hotfix branch is created using the 1.0 tag (unlike GitFlow which would have used the “master” branch for this). The branch is called “hotfix-1.0.1”. The version file is updated to “1.0.1-SNAPSHOT” and committed. The branch is pushed The defect is fixed (which takes 10 commits) The defect is tested Now we need to merge to master and claim the rightful version number.\nThe code is merged to master On master the version is updated to “1.0.1” The change is committed The commit is tagged “1.0.1” The commit and tag are pushed to origin Feature 1 Started at the same time as the Hotfix.\nA feature branch is created using master’s HEAD (currently 1.0.0) is created named “feature-1”. The version file is updated to “1.1.0-SNAPSHOT” and committed. The branch is pushed The feature is added (which takes 3 commits) Now we need to merge to “master” and create a release, but the Hotfix was already merged.\nThe code is merged to master, but fails because of a version file conflict The merge is reverted. The feature branch is checked out and “master” is merged back into the feature branch. The conflict is present again. It is fixed by keeping our “1.1.0-SNAPSHOT” version. Note: we did this because our version was greater then what was in master; see “Feature 2” for what to do when this is not the case. We retest before merging to master. We attempt the merge to master again. There are no conflicts so we bump the version number as we did with the Hotfix; becoming 1.1.0. Feature 2 Feature 2 logically started on the 0.9 version so has the 1.0.0-SNAPSHOT version. This feature is not finished until after feature 1 has been merged.\nMaster has been pulled Master is merged into the feature branch, with a conflict on the version file. The conflict is fixed by keeping master’s version: 1.1.0. The version number is bumped to 1.2.0-SNAPSHOT (as if this was a new branch based on the current master) The code is retested The branch is merged into master (as with feature 1 and the hotfix becoming 1.2.0. Multiple Same Versions There is a chance when merging a feature it will not create a merge conflict because it is trying to claim the same version as what is already in master.\nThis is why it is important to ALWAYS do the following when merging features:\nMerge master back into the feature branch before merging the feature into master. Manually update the version number if no merge conflict happened. ","lastmodified":"2016-02-02T00:00:00-05:00","tags":null,"title":"Gitflow Simple"},"/blog/20160423-packer-ova":{"content":"Recently I have started using Packer to build AMI images. It works like a champ, but then I tried to make VMWare images and it produced machine images, not machine exports. This makes the exports nearly useless. However, with a little post-processing magic this can be fixed.\nThere is a GoLang based ovftool post processor that already does this, but it needs to be compiled and put into the packer plugins dir. Nothing that the plugin does requires GoLang, so I whipped up a quick bash version that I could include with the packer script.\nVMWare comes bundled with a tool called ovftool which will convert a machine image into a OVA file. It has a lot of useful options so I recommend you reading the manual page. Basically it take a vmx file and its associated files and makes a OVA: ovftool sourc.vmx dest.ova.\nI turned this into a script that can be called as a post processor from packer. There are a number of protections, but the important items are:\nline 5: Ensures ovftool is on the path line 19: This avoids an interesting behavior (read bug) of Packer where the the inline script is called multiple times; once per file in the output directory. line 33: Removes the floppy which is used to install some VMWare drivers. line 37: Removes the CD-Room that is also used to install some VMWare drivers. line 42: Uses ovftool to create an OVA. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #!/bin/bash # Make sure ovftool is in the path. # On the mac: PATH=/Applications/VMware\\\\\\\\ Fusion.app/Contents/Library/VMware\\\\\\\\ OVF\\\\\\\\ Tool/:$PATH # I need to be given the output path and vmname. if [ -z \"$1\" -o -z \"$2\" ]; then echo \"output path and vm-name are required\" exit 1 fi DIR=$1 NAME=$2 cd $DIR # I may be called multiple times. If so bail early if [ -f \"${NAME}.ova\" ]; then echo \"OVA already created, skipping\" exit 0 fi if [ ! -f \"${NAME}.vmx\" ]; then echo \"no ${NAME}.vmx file found\" exit 1 fi # bail on error set -e # remove floppy sed '/floppy0\\\\\\\\./d' ${NAME}.vmx > 1.tmp echo 'floppy0.present = \"FALSE\"' >> 1.tmp # remove CD sed '/ide1:0\\\\\\\\.file/d' 1.tmp | sed '/ide1:0\\\\\\\\.present/d' > 2.tmp echo 'ide1:0.present = \"FALSE\"' >> 2.tmp mv 2.tmp ${NAME}.vmx ovftool -dm=thin --compress=1 ${NAME}.vmx ${NAME}.ova Add this script to the packer JSON files.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 { \"variables\": { \"output_path\": \"output\", \"vm_name\": \"data-collector\", ... }, \"builders\": [{ \"output_directory\": \"{{ user `output_path` }}\", \"vm_name\": \"{{ user `vm_name` }}\", ... }], \"_comment\": \"Create an OVA because packer cannot.\", \"post-processors\": [{ \"type\": \"shell-local\", \"inline\": [\"post-ova.sh {{user `output_path`}} {{user `vm_name`}}\"] }] } ","lastmodified":"2016-04-23T00:00:00-04:00","tags":null,"title":"Packer OVA"},"/blog/20161203-managing-base-docker-images":{"content":"Docker is a great way to package your code such that you can be sure it will run on any machine that has docker installed. However, maintaining your docker containers and publishing them to docker hub can be a bit of a challenge. The following are two ways I do it.\nOption 1: Let docker hub do it If your needs are simple then you can have Docker Hub monitor a repo and rebuild the docker file when you push a changed to the directory.\nDocker hub maps branches to tags with “master” being the “latest” tag. If you want more docker tags (maybe multiple versions) then you will need to create multiple branches and reconfigure Docker Hub.\nOption 2: One repo to rule them all The option that I generally go for is a single repo to manage all my docker containers. Where the directories map to the docker repos and to the tags. I do this because you “should” be separating your packaging from your source, and I like to keep similar things organized.\nWith this method you do not need to create the Docker Hub repos. They will be created by docker push from the build.sh, but you will need to manually edit the repo information on Docker Hub to make the repo useful for others.\nIf you wanted to automate it then you will need a CI service which allows docker images and Docker-next-to-docker. Setting up automation is beyond the scope of this entry.\nThe docker-image repo directory would look a bit like this:\n- / - jkamenik/ - hugo/ - 0.15/ - Dockerfile - entrypoint.sh - latest -> ./0.15 - README.md - my-private-repo.com/ - emacs - 25.1 - Dockerfile - build.sh The highest level directory (“jkamenik”) is the Docker hub account name. Or it could be a private docker repo. build.sh has not been tested against private repos, but it should work.\nThe 2nd level directory (“hugo”) is the docker image name: “jkamenik/hugo”. Within this directory is also where I put the README.md. This is because a Docker Hub Repo’s information is independent of a tag; so it makes sense for me to manage it in the same way. After the first creation, or an update to the README, I copy and paste this information into the Docker Hub repo’s information.\nThe 3rd level directory (“0.15”) is the docker tag: “jkamenik/hugo:0.15”. Also, the 3rd level directory can be a symlink, which is useful when tagging “latest” to a specific version.\nThe 4th level is where the Dockerfile and any supporting files go. A common pattern for me is to provide a script which will be the entry point. The behavior of this script is generally to check the argument list and if they look like program arguments blindly pass them to the default program. If they don’t look like program arguments then execute them as the entry point.\n1 2 3 4 5 6 7 8 # execute hugo without args $ docker run jkamenik/hugo # execute hugo with \"-D\" $ docker run jkamenik/hugo -D # execute bash $ docker run jkamenik/hugo /bin/bash The build.sh file takes 0 or 1 argument. If there are 0 arguments then it will build all directories. If there is 1 argument traverse that path and build only images there. For example:\n1 2 3 4 5 6 7 8 # build all $ ./build.sh # build all private repos $ ./build.sh my-private-repo.com # build only the 0.15 verion of hugo $ ./build.sh jkamenik/hugo/0.15 To see this all in action see https://github.com/jkamenik/docker-images","lastmodified":"2016-12-03T00:00:00-05:00","tags":null,"title":"Managing Base Docker Images"},"/blog/20171021-docker-details-dumb-init":{"content":"If you don’t control the “init” process of docker then you are doing it wrong. But don’t worry there is an easy fix. Before I explain the solution, I should explain the issue. Almost every process you run in Linux will likely run at least 1 child process. And Linux expects that every parent will properly care for its children by propagating kernel signals like SIGTERM, and by cleaning up child zombie processes. If all else fails the Linux init process will do that on behalf of Linux and all is happy.\nHowever, programmers generally don’t know the requirements of dealing with child processes, and linux clean up after itself so unless you already know what to do testing won’t show issues. The issue comes because Docker doesn’t provide an init process for the container, so your child processes will not get signals, zombies will be created, and eventually things will terminate uncleanly or hang indefinately.\nSolution: Use dumb-init dumb-init provides a very small init runtime that deals with signals and zombie processes. And nothing else! It is a tiny 45Kb statically compiled and will work inside any docker container as the entrypoint.\ndumb-init has the ability to do signal-rewriting which is very important if you are using apache which uses SIGWINCH for a graceful shutdown or nginx which uses SIGQUIT. Many other programs also use different signals then TERM to mean graceful shutdown.\nAnother nice benefit of dumb-init is that it will terminate the container immediately on any of the termination signals, even if the child processes ignore the signals. This prevents stall-out of container termination in Kubernetes, Docker Swarm, and Docker Compose.\nTest your containers To test your containers send it the SIGHUP, SIGINT, SIGTERM, SIGUSR1, and SIGUSR2 signals to your running container. All of them should immediately start the shutdown process.\nLets assume you have a container like the following container\n1 2 3 4 5 6 7 FROM alpine:3.5 COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh RUN apk add --no-cache bash ENTRYPOINT [\"/entrypoint.sh\"] The entrypoint.sh looks like this. It will print the signal it gets but only exit cleanly if it gets USR1. I also added a 2s wait on USR1 to simulate a graceful shutdown. All the signal handlers should be called in turn until one exists the program.\n1 2 3 4 5 6 7 8 9 10 11 #!/bin/bash trap \"echo TERM\" TERM trap \"echo HUP\" HUP trap \"echo INT\" INT trap \"echo QUIT\" QUIT trap \"echo USR1; sleep 2; exit 0\" USR1 trap \"echo USR2\" USR2 ps aux tail -f /dev/null You can build the container with a command like docker build -t my_container. If you run it with docker run --rm -ti --name my_container my_container you will get the following output:\nPID USER TIME COMMAND 1 root 0:00 {entrypoint.sh} /bin/bash /entrypoint.sh 7 root 0:00 ps aux You can send any signal you want to the container via docker kill -s HUP my_container (replace HUP with various signals). None of the signals are printed! To double check run docker stop, which will sent TERM, then wait 10 seconds before sending KILL.\n$ time docker stop my_container my_container real\t0m10.761s user\t0m0.011s sys\t0m0.015s So here we can tell that stop waited the full 10s and still needed to send KILL.\nFixing the issue Changing nothing about /entrypoint.sh we can fix this by updating the Dockerfile as follows.\n1 2 3 4 5 6 7 8 9 10 11 12 13 FROM alpine:3.5 COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh RUN apk add --no-cache bash # Change 1: Download dumb-init ADD <https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64> /usr/local/bin/dumb-init RUN chmod +x /usr/local/bin/dumb-init # Change 2: Make it the entrypoint. The arguments are optional ENTRYPOINT [\"/usr/local/bin/dumb-init\",\"--rewrite\",\"15:10\",\"--\"] CMD [\"/entrypoint.sh\"] Again start the container and run docker stop\n$ time docker stop my_container my_container real\t0m2.778s user\t0m0.011s sys\t0m0.013s Here you can see it exists immediately after the simulated graceful shutdown meaning it got and processed the USR1 signal, even though it was sent the TERM signal. The output of the container is\nPID USER TIME COMMAND 1 root 0:00 /usr/local/bin/dumb-init --rewrite 15:10 -- /entrypoint.s 7 root 0:00 {entrypoint.sh} /bin/bash /entrypoint.sh 8 root 0:00 ps aux User defined signal 1 USR1 0 In case you want to double check that TERM was actually used we can use docker kill -s TERM to explicitly send the TERM signal:\n$ docker kill -s TERM my_container my_container PID USER TIME COMMAND 1 root 0:00 /usr/local/bin/dumb-init --rewrite 15:10 -- /entrypoint.s 7 root 0:00 {entrypoint.sh} /bin/bash /entrypoint.sh 8 root 0:00 ps aux User defined signal 1 USR1 0 We still get USR1 as the signal.\nHonorable Mentions The following are worth mentioning, though I don’t use them.\ntini tini is an alternative to dumb-init. It is a few months older then dumb-init but doesn’t provide the signal rewriting that you are going to need for many of the programs you will want to run.\nIt is also 850Kb for the statically compiled version (vs 45kb for dumb-init). Not a huge number, but given it has fewer features it isn’t worth the bloat.\nAlso, if you do use tini remember to use the -g options so that all child processes are signaled, like would be done from init during a shutdown. This is the default for dumb-init but needs to be enabled for tini.\ndocker run –init The --init flag was added to docker 1.13 to run tini as the init process before the ENTRYPOINT is executed. Its a cute addition, but isn’t used by Kubernetes, Docker Swarm, or Docker Compose. This might be fixed in the future, but for now it is best to ignore this option.","lastmodified":"2017-10-21T00:00:00-04:00","tags":null,"title":"Docker Details - Dumb Init"},"/blog/20250406-wikilinks":{"content":"Wikilinks are a standard of many / most wiki software. However, Hugo does not have support for them. They have a simple form of Page Title or Display Text, which makes them very useful for quick linking. This is how I implemented something like them.\nShortcode Proof of Concept First, let’s start with a shortcode that gets us most of the logic. The being that shortcode { {% wl \"Hugo Wikilinks\" %}} would link to this page.\nSome examples\nwl \"Hugo Wikilinks\": Hugo Wikilinks wl \"Hugo Wikilinks\" \"Another name\": Another name wl \"hugo wikilinks\": Hugo Wikilinks wl \"invalid\": invalid wl \"FOO\" \"bar\": bar Note\nFor ease we’ll use the markdown form of calling shortcodes: % instead of <.\nThe contents of layouts/shortcodes/wl.html:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 {{- $title := .Get 0 }} {{- $display := .Get 1 | default \"\" }} {{- $page := \"\" }} {{- range .Site.RegularPages }} {{- if eq (lower .Title) (lower $title) }} {{- $page = .}} {{- if eq $display \"\" }} {{- $display = .LinkTitle }} {{- end }} {{- end }} {{- end }} {{- if ne $page \"\" }} {{- printf \"[%s](%s)\" $display $page.RelPermalink }} {{- else }} {{- $display | default $title }} {{- end }} Line 1: Gets the Title of the page for comparison 2: Gets the display value override 5 - 12: Searches all regular pages (ones with valid titles) for the one that matches 8 - 10: Uses the Page’s Link Title if an override is not provided 14 - 18: Prints either the wiki link or the display string or the title This is a 90% solution. However, we can make it the 100% solution with a lot more logic. So let’s leave it here for now.\nIf you are interested in more of a solution then milafrerichs/hugo-wikilinks has a good start.","lastmodified":"2025-04-06T00:00:00-04:00","tags":null,"title":"Hugo Wikilinks"},"/blog/20250418-kustomize-rollout":{"content":"Kustomize is a tool built into kubectl which helps in the management of YAML. It does a lot of things, but one of the major ones is having overlays per deployment. It is not uncommon to have a single base and a rollout per deployments. However, this can cause issue when you need to fix your base, as it will happily update all your overlaid environments en mass; which is less then ideal. Here is how I have fixed that for my deployments.\nSetup Let’s say we have the following kustomize layout. It is a simple deployment with a service and ingress. There are 4 deployments; the 3 standard stages (dev, staging, prod) plus a local testing overlay meant to be deployed manually to a local instance.\napp base deployment.yaml ingress.yaml kustomization.yaml service.yaml overlays dev kustomization.yaml local kustomization.yaml prod kustomization.yaml staging kustomization.yaml Option 1: Patches In this first option everything that makes a deployment unique is only tracked in the overlay via patches. This is very good for simple patches like the following:\n1 2 3 4 5 6 7 8 9 10 11 12 # overlays/dev/kustomization.yaml resources: - ../base patches: - patch: |- - op: replace path: /spec/rules/0/host value: dev.example.com target: kind: Ingress name: app However, things are little more annoying if you have to exclude or remote items. There is no way in kustomize to delete entire objects so the only way to handle this in each overlay is via full object copies. If however, you have a large discrepancy between an env or a lot of object then this can be error prone.\nOption 2: A/B Bases Note\nThis style is more complicated because it prevents accidental rollouts. It should be reserved for when the deployment maturity requires it.\nIn this style the base has a, b, provider-a and provider-b bases as well as all the standard overlays plus a local overlay that cannot have provider specific objects.\napp base a deployment.yaml ingress.yaml kustomization.yaml service.yaml provider-a backend-config.yaml cert.yaml external-secret.yaml frontend-config.yaml kustomization.yaml b deployment.yaml ingress.yaml kustomization.yaml service.yaml provider-b backend-config.yaml cert.yaml external-secret.yaml frontend-config.yaml kustomization.yaml overlays dev kustomization.yaml local kustomization.yaml prod kustomization.yaml staging kustomization.yaml A standard rollout is as follows:\nEvery overlay is pointing at *a local and provider-a directly at a dev, staging, and prod at provider-a Create / update b to be what is in a Point local to b Update b until it works for local Create / update provider-b based on provider-a Make sure that provider-b points to b Point dev to provider-b Update provider-b until it works for dev Roll out the changes to staging and prod (optional) Delete a and provider-a Blast Radius Diff\nBecause there are going to be so many file changes with every update it is useful to create a script that can diff the output of kustomize between what is currently deployed (usually the main branch), and what is currently on the branch. This is the only way to get a real idea of the blast radius of the change.\nOption 3: Versioned Helm Charts The final option - which is complicated as it introduces a new tool - is to produce a versioned [helm chart](/radar/languages/helm-chart/). Then use that reference and a values file in each overlay. In this case, because the helm chart is the base, no separate base is needed.\napp overlays dev kustomization.yaml values.yaml local kustomization.yaml values.yaml prod kustomization.yaml values.yaml staging kustomization.yaml values.yaml Each kustomizaton.yaml would look like this:\n1 2 3 4 5 6 helmCharts: - name: app includeCRDs: false valuesFile: values.yaml version: 3.1.3 repo: https://oci.example.com/repos/app Separately you will need a repo for the helm chart with a CI pipeline that pushes a versioned OCI image to a repository. To rollout a change you update the version flag and make any correction needed to the values file.\nNote\nIt is not uncommon to do local testing directly from the Helm chart and have a special dev cluster for manually testing the helm chart before it gets rolled out. This means that the local vs provider specific objects has to handled by logic exposed in the values.yaml.\n1 2 3 4 5 6 7 8 9 10 # values.yaml provider: \"local\" # only include the CRDs valid for local deployments # or provide explitic disable flags disableExternalSecrets: true disableManagedCertificates: true # hardcoded secrets are now needed secrets: someSecret: key: some-secret-value Don’t use chartHome\nKustomize does support a chartHome option which will use a local file path to find the helm chart. Don’t use it, as it is worst of all worlds. You have to manage it in A/B style or you have to add the stage logic to the chart directly which means you cannot truly test it before roll out. It will bite you.","lastmodified":"2025-04-18T00:00:00-04:00","tags":null,"title":"Kustomize Rollout"},"/blog/20250514-rag-pipeline":{"content":"A Retrieval-Augmented Generation (RAG) pipeline is a technique for interfacing with LLMs that helps to:\nAdd Context Improve Accuracy Check / Filter hallucinations Preserve Privacy Add Value In this age where LLMs are becoming ubiquitous you will very likely need to create one of these sooner or later.\nBasic Flow flowchart LR User((User)) Vector[(Vector DB)] LLM{{LLM}} Embedding[Embedding Processor] Embedding -->|A| RAG -->|B| Vector Vector -->|2 & 4| RAG -->|5| User User -->|1| RAG -->|3| LLMThere are two distinct flows that need to be performed. The first is the embedding flow, which reads and stores data not directly accessible to the LLM. It vectorizes the data and hands it to the RAG for storage in the Vector DB. Following Single Responsibility Principle, it is important that the RAG be the only system with direct access to the Vector DB.\nSeparately the Query flow starts with the user asking a question. The RAG system may (or may not) pull data from the Vector DB to augment the query with context the LLM doesn’t have. Care has to be taken to ensure that no IP is leaked to the LLM because queries are considered public and may be used in future LLM training. After the LLM response is returned the RAG can additionally augment the response with embedded context from the Vector DB before handing it back to the user.\nRAG Service The RAG service should be a simple HTTP service that has 2 types of endpoints. The first type will be the CRUD API for the embedding flow. The second is the streaming API for the query flow.\nEmbedding API The Embedding API should be your standard CRUD operations. You would be well served using an OpenAPI server. If at all possible we recommend taking the raw data in the API and then vectorizing it in the service for storage in Vector DB.\nQuery API The Query API should “stream” the response. There are a few ways to do this which will effect what deployment options you have. One of the easier ways - from the server perspective - is websockets. However, not all loadbalancers or WAFs supoport that. Another option is a Cursor Keep Alive, though that comes at the disadvantage of requiring a separate DB to keep track of the cursors.\nDeployment Options Your simplest option is a serverless function or container. That service would take care of the API endpoint and then you largely just have to make sure that your code completes before the service timeouts.\nA Kubernetes Deployment is another viable option. Expose the endpoimt via an Ingress though a high performance IngressController and you should be all set.","lastmodified":"2025-05-14T00:00:00-04:00","tags":null,"title":"RAG Pipeline"},"/blog/20250615-blast-radius":{"content":"Context is everything, and understanding Blast Radius is crucial for providing the necessary context when assessing risk as a DevSecOps professional.\nBlast Radius - simply put - is how much of the infrastructure is touched when a change is made. The higher the blast radius the higher the risk. And in the world of IaC that usually means it is caused by a change in IaC code. So it would be natural to assume that a large change in blast radius would be caused by a large change in IaC, but that is often not the case. There is only an indirect relationship between IaC lines changes and infrastructure changes.\nBut how do you protect yourself?\nYou measure blast radius, then track it using Key Performance Indicator (KPI), and finally design to minimize it.\nMeasuring Blast Radius The best way to measure Blast Radius is by using the IaC tool itself. A naive approach is to simply count the number of changes. However, that doesn’t include any kind of risk assessment. Different types of change represent different risks. Therefore, my recommendation is to calculate it as follows:\n+1 for any item added or deleted, but not replaced If possible, consider the underlying provider’s objects and not what is in IaC. For example, in terraform an aws_s3_bucket might have a logging block. Those are 2 separate objects in AWS and thus should be counted separately. As a side note, this is why the logging block is deprecated and the aws_s3_bucket_logging object should be used instead. +3 for any item changed in place They are higher risk targets as they are presumably already in place and working so a change is a higher risk +5 for any item that is replaced. They are already working, and any replace is likely to result in data loss. Add up all the changes and that represents the risk of the change. This can then be tracked over time to indicate the relative risk of changes.\nOptional items The following items are harder to know because sometimes the IaC won’t know. But if you can track them, you should.\n+7 for any case where the IaC change would not be respected. That is, the IaC is changed, but it won’t effect the underlying infrastructure. If this doesn’t result in an error then the IaC tool then it is a really high risk because you’ll assume everything is ok, but it very much isn’t. S3 bucket encryption is an example of this. If the flag is enabled only new files are encrypted. Older files remain unencrypted without any way to know. +9 if the change would cause loss of data This usually is only discovered during that oh-shit moment when everything fails and you have to trigger Disaster Recovery. Once this type of change is known it should be documented as the kind of change that shouldn’t be made. Policy-as-Code tools are good for preventing this. At the time of this writing, AWS EFS encryption is an example. It would happily remove all existing files when this flag is changed. Blast Radius KPI The following are the KPIs you need to track and publish:\nBlast Radius (See above) - This tells you the risk of each apply IaC lines changed - This tells you the programmer’s intended number of changes for each apply. It is important to track the lines changed between two applies of the IaC, not just the lines changed per commit. Each line changed, added, or deleted counts as +1. Resources Under Management (RUM) - This is a count of the number of objects described by the IaC at the time it is applied. Since IaC often has means to reduce code duplication (like modules) this should be based on fully expanded resources, not just what is in the IaC files. IaC Code Efficiency ($\\frac{IaC\\ resources}{1K\\ line\\ of\\ code}$) - The number of resources should be at the plan or apply stage. The lines of code should be measured based on the workspace. It is measuring how effective you are at using coding techniques to manage your IaC resources. Change Risk (min, max, mean, median, & mode of $\\frac{Blast\\ Radius}{IaC\\ Changes}$) - This tells you how much of your infrastructure is changing per change in IaC. High numbers indicate that relative small changes in actual code cause high changes in infrastructure. You need to balance IaC Code Efficiency and Change Risk to maximize your operational speed.\nDesigning for Blast Damage Each situation and business model is different. So there is no one size-fits-all solution here. The design goal should be to know what the maximum potential effect of a change could be and to ensure it is within an acceptable tolerance given the business. Then design an IaC layout and processes that maximizes the IaC Code Efficiency without increasing the potential blast radius outside acceptable tolerances.\nBalancing these two competing ideas is difficult and constantly needs to be revisited and adjusted. Below are some ideas that might help you.\nSimple Base Let’s say you’re using a code gen tool like Kustomize. This uses a base + overlays. Let’s say there are overlays for dev, staging, and prod. Any change to base affects all envs, thus has a large blast radius. Change to overlays only affect those overlays. Therefore they have a smaller blast radius.\nA good technique to reduce blast radius is to apply the changes to each overlay individually. This maximally reduces Blast Radius by maximally increasing code duplication. To reduce code duplication, once each overlay has all the changes, move them up into base so that any new envs also benefit. The idea is to keep the base as simple and minimal as possible, and copy and paste the required changes between overlays.\nYou might even consider splitting your monolith across multiple bases so that each can release independently.\nA/B rollout A more extreme version of the above - but ultimately easier to maintain - is an A/B style rollout. Using the Kustomize example again, you’d create a second copy of base called base-b. You’d modify it as needed and point each overlay to it in turn. Once all overlays are pointed at base-b you can erase base.\nYou’d have as many bases as you’d have in-process rollouts up to a maximum of the number of envs you have. Therefore it is important to track the roll outs carefully.\nCare also has to be taken with PRs since the actual line change will look huge but the relative change will be small.\nCheck-in Fully Rendered Code Similar to the A/B style, but more broadly is to use code gen tools like CUE to maintain an easier to manage base template then fully render it out and check-in that code. In this case the CI/CD pipeline should only ever run off the fully rendered code, and the developer controls what changes get committed and thus the blast radius.\nFor early teams this is likely a good balance. But as the team scales the amount of generated IaC per small change will be untenable. So just keep that in mind. This model is ultimately a path with limited long-term scalability (an “evolutionary dead-end”), but it could serve you for years before any issues arise.\nVersioned Artifacts The most effective approach to handling Blast Radius is through the use of Versioned Artifacts and rolling out new versions. All roads will eventually lead to this method; however, there is quite a bit of effort involved in setting up a repository for the artifact, CI pipelines that include adequate testing, and artifact storage.\nWhile this gives you the benefit of being able to independently test the artifact before release, the entire process does involve some amount of operational overhead. Therefore it is only recommended for mature organizations for the items that MUST remain stable.\nBranches and/or Tags Sort of a lazy man’s versioned artifacts is using branches or tags. Code can be merged between branches as needed to rollout changes, but you do not have to do things like setting up new repos, CI pipelines, or Artifact Repos. Folks also generally know how to merge between branches better then they understand how to release artifacts. Therefore it is a smaller conceptual load and easier to pick up.\nHowever, there are some major downsides:\nMerging is manual so forgetting to merge changes across branches will be common. There is no relationship between branches, thus it won’t be clear what or how changes can and should be made, or where things are deployed. It would be entirely possible to have changes not flow though a standardized process. Environments will tend to drift both making future merging difficult. ","lastmodified":"2025-06-15T00:00:00-04:00","tags":null,"title":"Blast Radius - Critical Context"},"/contentindex/index.json":{"content":"","lastmodified":"0001-01-01T00:00:00Z","tags":null,"title":"Content Index"},"/linkindex/index.json":{"content":"","lastmodified":"0001-01-01T00:00:00Z","tags":null,"title":"Link Index"},"/radar/languages/container-structure-test":{"content":"Container Structure Test provides a powerful framework to validate the structure of a container image. These tests can be used to check the output of commands in an image, as well as verify metadata and contents of the filesystem.\nIf you are building containers then this is a critical tool.\nTests are defined in a YAML file and then run via\n1 container-structure-test test --image <image> --config <testfile> There are three types of tests that can be performed:\nCommand Tests - Execute a command and check its output File existence tests - Check a file is or is not present File content tests - Check a file has or does not have certain content Metadata test - Check that the metadata of the container is correct ","lastmodified":"2023-12-17T00:00:00-05:00","tags":null,"title":"Container Structure Test"},"/radar/languages/dev-container":{"content":"Dev Container was a system born in VS Code that allowed the IDE to boot a container and use it as its development runtime. The benefits of this are huge: no longer does each developer need to set up a development machine, no longer does one developer’s effort have to be manually replicated by all others, no longer is there a skew between dev, CI, and production. There simply is shared Infrastructure as Code (IaC) which is used to build all of those. And because of these benefits, Dev Container is very much in the adopt ring.","lastmodified":"2025-04-23T00:00:00-04:00","tags":null,"title":"Dev Container"},"/radar/languages/go":{"content":"Go is a language that google created to handle its unique scaling needs. It turns out that designing for those needs, eliminates the performance and scale cliff that almost everyone experiences. Since the language itself is fairly simple this has made it a boon for anywhere large scale and/or highly concurrent programming is needed. For this reason it has become the de facto language for many DevOps tools, so if you are in that space then you should know it.\nGoLang\nBecause “Go” is very generic “GoLang” can be used in search.\nOne of its core strengths is an active community of maintainers and a Special Interest Group (SIG) that forces a workable reference implementation of all things before they can be added to the language. Unlike many other languages where a theoretical feature is added only to be poorly thought out, the SIG forces a working example to be implemented in a fork of the language before it is brought in.\nThis can make it frustrating for those familiar with other language constructs like package management, inheritance, or generics. Go seems to move slowly and when picked the solutions don’t always look like they do in other languages. However, this is a strength of Go. The solutions that Go comes up with scale extremely well because they are only added to the language if they do.\nTake for example error handling in Go. It is implemented as an interface, where any object that has a function Error() string is considered an error. This means:\nErrors are treated as variables, and don’t need special handling. Functions often return both an object and an error, leveraging Go’s multiple-return feature. This encourages handling errors immediately after they are returned. To check if a file is closed, you can use a simple comparison like if err == io.EOF, which checks if the error is exactly io.EOF. io.EOF is a singleton defined in the io package. So you are asking if the memory address of the error is that of io.EOF. In many cases, when you know the context of the error, this is enough.\nHowever, there are cases where the exact context isn’t fully known, leading to much debate on how to handle these cases. Go came up with the wrapped errors. Functionally it acts kind of like exception inheritance in other languages. Let’s say you want to print the file name in the error, you would wrap the error: return fmt.Errorf(\"file :%v, %w\", file_name, io.EOF). This breaks err == io.EOF since the new error is not io.EOF. So two functions to check wrapped errors were added: Is and As.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func usingIs() { if _, err := os.Open(\"non-existing\"); err != nil { if errors.Is(err, io.ErrNotExist) { fmt.Println(\"File does not exist\") } } } func usingAs() { if _, err := os.Open(\"non-existing\"); err != nil { var pathError *fs.PathError if errors.AS(err, io.&pathError) { fmt.Println(\"Failed at Path:\", pathError.Path) } } } which will check if the current error is or wraps io.EOF. In this way it is a drop in replacement for ==, plus works with error wrapping.","lastmodified":"2025-05-08T00:00:00-04:00","tags":null,"title":"Go"},"/radar/languages/go-template":{"content":"If you are a Go programmer then Go Templates makes a lot of sense. But Helm, and Hugo are some of the more popular tools that use it, so its usage is relatively specialized. For this reason you really have to assess if there is any need.\nThings to know The language is fully described in the programming documentation. Basically, it reads text for embeds. Anytime you want to drop to code you use {{ ... }}. The output of that block is then inserted in place. Data can be accessed with $ (main object), or . (current scope). There are a relatively small set of built-ins like range, or block.\nThe main object ($) and all functions have to be registered directly in the go code. Meaning that different usages have very different behaviors. Sprig is a very common library of template functions that should be loaded to make the templates really useful.\nExample 1 2 3 4 5 6 7 8 9 10 import ( \"github.com/Masterminds/sprig/v3\" \"html/template\" ) // This example illustrates that the FuncMap *must* be set before the // templates themselves are loaded. tpl := template.Must( template.New(\"base\").Funcs(sprig.FuncMap()).ParseGlob(\"*.html\") ) template.FuncMap is a map[string]any and can be used to add any functions to the list, including internally developed ones.","lastmodified":"2025-04-24T00:00:00-04:00","tags":null,"title":"Go Template"},"/radar/languages/helm-chart":{"content":"Helm Charts are the artifact that Helm reads and installs in a Kubernetes cluster. They are written in Go Template format, and are compiled into OCI compatible images, which means they can be stored in any OCI compliant Docker registry, of which there are a plenty.\nWhile Helm itself is a MUST if you are in the Kubernetes ecosystem that doesn’t extend to maintaining your own Helm Charts. They are nessessity to understand but there are limited use cases where you should maintain them your selves. The real only reason to do that is if you are providing customers the ability to install on their own clusters. For all other cases Kustomize is a better choice.","lastmodified":"2025-07-13T00:00:00-04:00","tags":null,"title":"Helm Chart"},"/radar/languages/yaml":{"content":"YAML is a more human readable format which is fully API compatible with JSON. It gained popularity as being the Declarative IaC language for things like Docker Compose, and Kubernetes.\nThere are some syntactic sugar like anchors and aliases and deep merging of maps which makes it easier to for humans to read. Additionally, many systems (like Helm) add a templating language on-top to make generating large amounts of YAML easier. And since Yaml is a subset of JSON, JSON Schema that can be used to validate complex YAML. Because of this ubiquity you should adopt it.\nThe creators of YAML also created YAMLScript as a templating language, but it is not wide spread yet.","lastmodified":"2025-05-10T00:00:00-04:00","tags":null,"title":"YAML"},"/radar/languages/yamlscript":{"content":"YAMLScript (YS) is valid YAML, however, it is not API compatible with YAML. Therefore, even if example.ys can be read by a YAML interpreter the output would not contain the same objects as the parsed YS. Currently no main stream programs use it, but we are optimistic that it might eventual replace Go Templates for things like Helm. But until that happens you have to assess if it suits your needs.\nAll YS files must start with !yamlscript/v0/data, or a SheBang (#!). This will cause non-compliant parsers to fail, and instruct compliant ones on which language to use for the rest of the file.\nExample Data 1 2 3 4 !yamlscript/v0/data base =: 42 example: int(base) will result in\n1 example: 43 Example Program 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #!/usr/bin/env ys-0 # Print the verses to \"99 Bottles of Beer\" # # usage: # ys 99-bottles.ys [<count>] defn main(number=99): each [n (number .. 1)]: say: paragraph(n) defn paragraph(num): | $bottles(num) of beer on the wall, $bottles(num) of beer. Take one down, pass it around. $bottles(num - 1) of beer on the wall. defn bottles(n): cond: n == 0 : 'No more bottles' n == 1 : '1 bottle' => : \"$n bottles\" main is the entry point and all command line arguments are split as function arguments. Builtins like each, say, cond take zero or more arguments and perform work given by their object definition. User defined functions are called using ().\nThis is a functional language and thus the program and object definitions are unordered. For example in cond the “=>” string indicates the default case; it is only placed last as a convention. This also means that all conditions have to be fully evaluated before the default case can be chosen. Not usually a issue, but something to be aware of.\nIn paragraph the | indicates that the next block is text. The $ in the block is interpolation.","lastmodified":"2025-05-08T00:00:00-04:00","tags":null,"title":"YAMLScript"},"/radar/platforms/cloudbees":{"content":"All marketing aside, CloudBees both provides a managed Jenkins solution as well as actively maintains Jenkins. If you are in the Jenkins ecosystem already then this is basically your only serious option. But, Jenkins has not kept up with the changes of the industry so it should be avoided if at all possible.","lastmodified":"2025-05-10T00:00:00-04:00","tags":null,"title":"CloudBees"},"/radar/platforms/docker-desktop":{"content":"Docker Desktop is a developer platform which can be critical if you are in the container ecosystem. This makes it especially useful for Dev Container based development. With the adoption of built-in native kubernetes, it becomes a critical solution by enabling seamless container orchestration, simplifying development workflows, and providing an integrated environment for building, sharing, and running containerized applications.\nFor larger orgs it comes with a hefty price tag, but the investment can be justified by the productivity gains, streamlined workflows, and robust features such as built-in Kubernetes support and containerized development setups.","lastmodified":"2025-04-23T00:00:00-04:00","tags":null,"title":"Docker Desktop"},"/radar/platforms/docker-swarm":{"content":"As of 2025 swarm maintenance has been to a separate company from the one that maintains Docker. We therefore recommend you avoid this platform.\nOriginally Docker Swarm was billed as an alternative to Kubernetes. However, when that didn’t get traction it changed course and retooled as way to bridge Docker Compose to a multi-machine setup. However, there are some issues with stateful data that - due to the lack of on-going support - will likely not be solved.","lastmodified":"2025-05-10T00:00:00-04:00","tags":null,"title":"Docker Swarm"},"/radar/platforms/jenkins":{"content":" Security Blackhole!\nThere is no way to properly secure Jenkins and make it Cattle not pets. It should not be allowed to touch production workloads, and where possible should be removed anywhere security is a concern.\nJenkins is the OSS version of Hudson and Hudson was originally designed to build Java projects. It predates DevOps and DevSecOps by a wide margin. While over the years there have been many attempts to make it compatible with the change in mindset, there are too many existing installations of it to make this possible. It is software that is best avoided, and other proper CI-CD tools used.\nIf you must use Jenkins then you MUST treat it as a Pet / Snowflake and manually manage it. I cannot tell you the number of Jenkins instances that either die on reboot, die on upgrade, or fail in a disaster recovery (DR) situation. By the time the issue is identified it is often easier to rebuilt the entire pipeline using an appropriate tool then it is trying to make Jenkins work again.\nAdditionally, absolutely DO NOT use the internal sensitive information store. First, it isn’t actually secure, and is easily reversed. Second, the information there cannot be properly managed, tokens cannot be rotated, etc… Finally, the ID of the sensitive info is tied to machine ID. So if the machine fails and you need to restore to a different machine then none of the ID will match and all the pipelines will have to be rebuilt by hand even if the pipeline was defined by IaC.\nInstead, consider using dedicated secret management tools such as HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault. These tools provide robust security features, including encryption, access control, and automated secret rotation, which are essential for managing sensitive information in CI/CD pipelines.","lastmodified":"2025-05-10T00:00:00-04:00","tags":null,"title":"Jenkins"},"/radar/platforms/jenkins-x":{"content":"Yet another option for using Jenkins places it doesn’t belong. Save yourself the trouble and use ArgoCD, or Argo Workflows if you plan on running your pipelines on Kubernetes.","lastmodified":"2025-05-10T00:00:00-04:00","tags":null,"title":"Jenkins X"},"/radar/platforms/kubernetes":{"content":"Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of containerized applications. Unlike many systems before it, Kubernetes is fully multi-cloud and truly planet-scale. At the time of this writing, there is a theoretical limit of a single cluster having 25 million nodes, with each node having thousands of CPUs and millions of GPUs. In practice, clusters tend to be much smaller than that, but they also tend to be very dynamic and scale as needed.\nIt uses a YAML based Declarative IaC syntax to orchestrate Containers. While there are alternatives like Nomad, Docker Swarm, and DCOS, realistically, they each have limitations that Kubernetes does not, making it likely that you will decide to (or be forced to) migrate.\nIf you want to avoid cloud vendor lock-in or operate in a Hybrid Cloud environment, Kubernetes is essential.","lastmodified":"2025-05-10T00:00:00-04:00","tags":null,"title":"Kubernetes"},"/radar/platforms/nomad":{"content":"Hashicorp Nomad bills itself as an orchestration system that is easier then kubernetes. While that is true there is less of a community, so your only option is Hashicorp commercial support. It is a good platform to know, but your mileage may vary for production workloads.","lastmodified":"2025-05-11T00:00:00-04:00","tags":null,"title":"Nomad"},"/radar/platforms/replicated":{"content":"Replicated is an air-gapped Kubernetes installer. If you live in that space then this is basically your only commercial option. However, you aren’t doing yourself any favors choosing Kubernetes. Therefore, for the majority of users, this option should be avoided.","lastmodified":"2025-05-19T00:00:00-04:00","tags":null,"title":"Replicated"},"/radar/platforms/system-initiative":{"content":"System Initiative is a visual DevOps tool. Think wysiwyg terraform.\nWhen it started in 2019, we were optimistic about its novel approach of using a hypergraph to perform all the work on a digital twin before applying it to the underlying infrastructure. We appreciate that it is an open source project, but the source reveals that the hypergraph is essentially JSON objects, and the digital twin is just JSON transformers that are applied to the in-memory version of that objects.\nWe are 5 years on, and novelty of that approach has waned as it struggles to implement useful features like support for anything but AWS, templates of reusable code, basic linting, or 3rd party integrations. Unless you are already invested in this solution we recommend avoiding this platform.","lastmodified":"2025-04-23T00:00:00-04:00","tags":null,"title":"System Initiative"},"/radar/platforms/tailscale":{"content":"Tailscale is a software defined VPN platform which uses Wireguard under the hood. If you have a need for a traditional VPN then Tailscale might be a easier solutions. However, in this day and age you are likely better served with a ZTNA solution, so we remain cautious.","lastmodified":"2025-04-23T00:00:00-04:00","tags":null,"title":"Tailscale"},"/radar/techniques/12-factor-app":{"content":"The 12-factor methodology is designed for building SaaS services that are declarative, clean, maximally portable, and minimally divergent. By following these principles, the code becomes more portable, testable, and manageable in production, without being restricted by language, platform, or framework choice. All services should adhere to these principles.","lastmodified":"2023-12-17T00:00:00-05:00","tags":null,"title":"12 Factor App"},"/radar/techniques/3-point-estimate":{"content":"The three-point estimation technique is used in management and information systems applications for the construction of an approximate probability distribution representing the outcome of future events, based on very limited information. It is very useful in estimating the scope of engineering projects as well.\nIf your team is struggling with estimating, this might be a viable alternative to simple effort points, which are common in Scrum. By taking a range of estimates, you can avoid unnecessary debates and over-analysis that often arise during estimation. Many teams will find it easier to estimate dozens of tasks in the same time it would have taken to estimate just a few with planning poker.\nEstimates Before we get to the math the 3-points in 3-point estimation are the following estimates. For each task these are the values you need to track. As developers we recommend estimating in whole number days. However, some teams choose 1/2 days or hours, but we strongly recommend against that.\na - The best-case. This is probably what an expert would say it is. Or someone outside of eng would think it should take. m - The likely case. This is what you’d arrive at with planning poker or some other consensus estimating technique. b - The worst-case. This is the “it could possible take more then…” number. The Math For each task that has been estimated calculate the weighted average and standard deviation:\n$E = \\frac{a + 4m + b}6$ $SD = \\frac{b-a}6$ Then calculate the confidence of the project by combining the estimates for each task:\n$E(project) = \\sum E(task)$ $SD(project) = \\sqrt{\\sum SD(task)^2}$ You can then convert this into a confidence interval for the project:\n68% = $E(project) \\pm SD(project)$ 90% = $E(project) \\pm 1.645 \\times SD(project)$ 95% = $E(project) \\pm 2 \\times SD(project)$ 99.7% = $E(project) \\pm 3 \\times SD(project)$ 95% is usually the target.\nWith spreadsheet this is pretty easy to do.","lastmodified":"2025-05-09T00:00:00-04:00","tags":null,"title":"3 Point Estimate"},"/radar/techniques/access-on-demand":{"content":"Access on Demand (AoD, a.k.a On-demand Access) is a technique in which no standing access is granted to sensitive resources like production systems. Instead there is a lightweight audited process to elevate access for a limited time period.\nTo meet even the most strict compliance frameworks all that is needed is the ability for the user to declare a reason for the increased access and the duration which is then logged. Therefore we recommend trying this technique with your existing SSO provider to see if it is viable.\nThere are many ways to implement this type of system, but a common that works for SAML and OIDC workflows is as follows:\nUser is granted the groups for which they get standing access and aod_<group> (for example aod_aws_prod_admin) for ones where they can increase access. A system is provided that allows the user to requires aod groups for a set time using. The reason given is logged. When the request is made a new session token is provided with the new group added, and a expiry timestamp no longer then the requested duration. The user uses the new token as they would any other token. Each system is looking for authentication using the non-aod groups (for example aws_prod_admin) and thus access is granted if appropriate. ","lastmodified":"2025-05-10T00:00:00-04:00","tags":null,"title":"Access on Demand"},"/radar/techniques/agile-software-development":{"content":"In order to ensure credit is given we won’t restate the manifesto here and instead redirect you to the Manifesto for Agile Software Development.. The software industry as we know it owes much to this manifesto.\nThe single most important thing is that we are people trying to deliver value and get paid. Keep practices that make this easier and possible. Dispose of those that get in the way and learn from your mistakes. This should be adopted by all teams as the foundation for their processes.","lastmodified":"2025-05-09T00:00:00-04:00","tags":null,"title":"Agile Software Development"},"/radar/techniques/cattle-not-pets":{"content":"Once upon a time we treated servers like pets. We gave them names, allowed them to become unique, and spend a lot of time caring for them individually. This didn’t scale so we increasing made the pets do more and more and it was catastrophic when they died.\nCattle are numbered, identical, fungible (interchangeable and replaceable without loss of value), follow instructions, and are replaced when they fail. This facilitates almost all modern software practices. Things like containers, Kubernetes, Software as a Service, or Cloud wouldn’t exist without this technique. Therefore it is an absolute must.\nReferences https://cloudscaling.com/blog/cloud-computing/the-history-of-pets-vs-cattle/","lastmodified":"2025-05-10T00:00:00-04:00","tags":null,"title":"Cattle Not Pets"},"/radar/techniques/chatops":{"content":"ChatOps means two distinct things: 1) sending alerts to a chat application, and 2) using a bot to receive commands from a chat app. Chat apps like Slack strongly recommend this model. They even include hundreds (maybe thousands) of integrations specifically targeted at these use cases. However, for development work, this approach is often a mistake. It is either ignored or, more likely, slows the team down due to constant distractions.\nIt was and is strongly recommended by tools that are in the chat space like Slack. However, in almost every case they are a bad idea and should be avoided for development work. Thus we put ChatOps in the hold ring. While it may be entertaining for generating memes, it is not suitable for critical tasks.\nChatOps for Alerting In DevOps / DevSecOps alerting is very important. It is the way that you know something is wrong and that your attention is actually needed. You need to leave that space clear for only the most important alerts and thus mixing that type of alerting with co-worker chats is a bad idea.\nChatOps for Actions Triggering automation from a chat program seems like a boon. Who wouldn’t want to force encrypt all their drives right before they walk onto an air plane for a 5 hour trip… Anyone in DevSecOps.\nAlso, it is really inadvisable to give your chat software access to your critical infrastructure. Therefore any investment made in this area will quickly fail a standard audit leaving you with no viable path. It is better to not fall in this trap to begin with.","lastmodified":"2023-07-23T00:00:00-04:00","tags":null,"title":"ChatOps"},"/radar/techniques/cloud":{"content":"At its most basic, “Cloud” just means someone else’s computer. More importantly, it refers to shared infrastructure that is managed and configured programmatically. This infrastructure can be used as a base for customers to build on. It is also known as Infrastructure as a Service (IaaS) or Platform as a Service (PaaS).\nHistorically, AWS was the first true cloud provider. Amazon sought to offset the operational costs of infrastructure that was heavily utilized during Black Friday and the lead-up to Christmas but remained largely idle at other times. Their revolutionary approach eliminated the need to consider physical hardware, replacing it with configurable options.\nMany years on and all the major cloud offerings have the same set of base offerings, like object storage, network attached storage, VMs. Then on top of that they also have ubiquitous managed offerings like Postgres, Kubernetes, and Serverless. Specifics are different, but the concepts are all the same. And for this reason, if you are a modern DevOps then you must the cloud concepts.","lastmodified":"2025-05-11T00:00:00-04:00","tags":null,"title":"Cloud"},"/radar/techniques/cloud-lift-and-shift":{"content":"Cloud Lift and Shift was a term coined in the early days of the Cloud. Companies wanted to migrate their datacenters to the cloud to lower cost, but they didn’t want to change anything about their workloads. This approach was largely unsuccessful at the time.\nCurrently, it has come to mean switching between similar services of the different cloud providers in a way to save cost. Very commonly it is migrating workloads (stateful and stateless), databases, and files between the different clouds chasing cloud credits. It is a losing game, and it is far better to lean into a Hybrid Cloud mentality where the effective workloads remain in the most effective cloud provider, and you bridge your cloud accounts securely.","lastmodified":"2025-07-10T00:00:00-04:00","tags":null,"title":"Cloud Lift and Shift"},"/radar/techniques/code-review":{"content":"A code review should involve both human (if possible) and machine review. Any opposition, such as unresolved comments, failed automated checks, or policy violations, should block the review from being merged. Luckily most Source Version Control (SVC) systems like GitHub, GitLab, and Gerrit have a code review process that is built into their merge process. It should be the entry point to the Continuous Integration process, and should be adopted by all teams.\nEven if you are a party of 1, having machine review centralized in a standard code review process will save you a lot of headaches.\nNominclature GitHub calls it a Pull Request, sometimes appreviated PR.\nGitLab calls it a Merge Request, sometimes appreviated MR.","lastmodified":"2025-05-11T00:00:00-04:00","tags":null,"title":"Code Review"},"/radar/techniques/declarative-iac":{"content":"Infrastructure as Code (IaC) is the idea that all infrastructure can (and should) be described as executable code. This allows machines to continually reconcile the code against reality and eliminate drift before it becomes costly. “Declarative” means that the IaC file has minimal logic and declares the end-state.\nDeclarative IaC should be adopted and CDKs and other forms of Imperative IaC should be avoided.\nOne of the biggest complaints about Declarative IaC is that it often leads to repetitive code. Additionally, the logic can sometimes be unclear or difficult to follow. However, most IaC tools offer code encapsulation features that address these concerns effectively.","lastmodified":"2025-01-05T00:00:00-05:00","tags":null,"title":"Declarative IaC"},"/radar/techniques/dry":{"content":"Don’t Repeat Yourself is a technique aimed at reducing repetition of code.\nWhile the assertion that repeated code is harder to maintain and therefore error prone is true, the way that DRY is implemented is often wrong and leads to increasingly complex code with more abstractions than necessary. It also leads to premature optimization and various other anti-patterns which increase the blast radius of changes.\nIn the IaC world, minimizing blast radius is paramount. Therefore a little repeated code here and there is a small price to pay for a robust infrastructure.\nResources https://en.wikipedia.org/wiki/Don%27t_repeat_yourself https://dev.to/ralphcone/please-do-repeat-yourself-dry-is-dead-1jbg https://dev.to/jeroendedauw/the-fallacy-of-dry ","lastmodified":"2025-01-05T00:00:00-05:00","tags":null,"title":"DRY"},"/radar/techniques/enterprise-ready":{"content":"Enterprise Ready is a series common features that are needed by enterprise companies, like Single sign-on, RBAC, and SLAs. If you are trying to attract enterprise customers then the features listed here are table-stakes. But more broadly they are good set of features to have in general.\nYou can think of it as 12 Factor App for businesses targetting Enterprise customers.","lastmodified":"2025-05-12T00:00:00-04:00","tags":null,"title":"Enterprise Ready"},"/radar/techniques/gitops":{"content":"GitOps is a key part of ShiftLeft. The idea is that the Pull Request (aka Merge Request) process that is common for developers is an ideal way to handle *-as-code items like IaC (Infrastructure as Code), PaC (Policy as Code), or SaC (Security as Code) items. Just as a developer PR would kick off a build pipeline, a GitOps PR would kick off an deployment pipeline. For this reason it is a strong adopt.\nBenefits of GitOps GitOps:\nIs able to describe complex multi-part pipelines in a single place Is able to track changes to config over time Is able to fully attribute change source (i.e., committer) and approvals Is able to design complex branching and merging behaviors easily Has readily available tooling Challenges with GitOps Even the best tool makes certain assumptions that have to be solved before the tool can be used to its full potential.\nGitOps:\nAssumes things can be described via code. This is getting better with tools like Terraform, Ansible, and Kubernetes but there are a lot of legacy out there. Separates identity from actions. Again mostly a legacy thing where the legacy attributes a human user to each action. CD pipelines are always triggered by machine so there is an extra step needed for attribution. Must have permissions checking and review at the file/directory level to ensure triggering by only correct folks. This is related to the separation of actions and identity. Since the CI will run using its identity and permissions GitOps can be a means to escalate privileges. The solution might be as simple as a CODEOWNERS files, or using a branch permission model. Must have a good means of handling sensitive info outside of Git. Should have a policy engine to check compliance. Something like Terrascan, or OpenPolicyFramework which can lint the change before it applied. Makes it difficult (but not impossible) for machines to effect change. Humans are good at handling conflicts, but machines are not. Also, adding a git repo mid-stream might be unnecessary as there are usually better ways to handle state between CD stages. ","lastmodified":"2023-03-30T00:00:00-04:00","tags":null,"title":"GitOps"},"/radar/techniques/hybrid-cloud":{"content":"We are big fans of Hybrid Cloud, if and only if you do it correctly. The worst thing you can do is flop back and forth between similar services across clouds (also known as Cloud Lift and Shift). However, if you utilize the best of each cloud’s offering and allow your developers to choose the ideal environment for their needs, then Hybrid Cloud can be a multiplier.\nIt is important that you bridge your Cloud accounts securely either via SDWAN, VPN Gateway, or Beyond Corp.","lastmodified":"2025-07-10T00:00:00-04:00","tags":null,"title":"Hybrid Cloud"},"/radar/techniques/imperative-iac":{"content":"Imperative IaC is using a programming language to generate the IaC code. This is often seen as a boon since the number one complaint about Declarative IaC is that it is not DRY enough. And being able to use the suite of programming refactoring tools seems great.\nHowever, the two main goals of IaC are decreased blast radius and security over time, which imperative IaC makes impossible. Almost all clients we have worked have regretted the choice of Imperative IaC tools like Pulumi and AWS CDK. While imperative IaC code might be smaller, the side effects of simple changes are vastly more complicated to understand, which makes responding to change risky, and error prone.\nMore often then note our first order is to us a tool like terraformer to generate raw IaC from the state of the cloud account and then painstakingly refactor the IaC correctly as it should have been originally. Though use of proper IaC refactoring technique we arrive code that is both clean and maintainable with minimal repetition, rendering promises of Imperative IaC moot and thus we move this to hold.","lastmodified":"2025-01-05T00:00:00-05:00","tags":null,"title":"Imperative IaC"},"/radar/techniques/inbox-pattern":{"content":"The inbox pattern is an effective means to guarantee delivery of a work item. You might be familiar with it from email. It is a highly effective way to make sure that work is complete, or retried until it completes. It is light weight and can be used effectively in much more complex systems like work queues, or workflow. It should be adopted before more complex designs.\nsequenceDiagram actor User box Service participant API participant TaskExecutor end participant DB as Database User ->> API: request API ->> DB: insert job API ->> User: accepted loop DB -->> TaskExecutor: get job TaskExecutor ->> TaskExecutor: do work TaskExecutor ->> DB: record results endEffectively, the service serializes enough information into the database to perform the work, along with job details such as the date and completion status. If the TaskExecutor might fail before completing a long-running job, use a reworkAfter timestamp instead of a simple boolean status. When the TaskExecutor picks up a job, set the reworkAfter timestamp to the current time plus twice the maximum allowable time. Any job with a null or expired reworkAfter timestamp can be reprocessed. Continue processing all jobs until completion, repeating as necessary.\nAKA Outbox Pattern\nThe outbox pattern is essentially the inverse of the inbox pattern. In this approach, the service writes outgoing messages or events to an outbox table in the database during normal operations. The TaskExecutor then reads from the outbox and sends these messages to external systems or services.\nFor example, consider an e-commerce application where order confirmations need to be sent to customers. When an order is placed, the service writes the order details to the outbox. A separate TaskExecutor processes the outbox, sending confirmation emails to customers. This ensures reliable delivery even if the email service is temporarily unavailable, as the TaskExecutor can retry sending messages until successful.","lastmodified":"2025-05-19T00:00:00-04:00","tags":null,"title":"Inbox Pattern"},"/radar/techniques/planning-poker":{"content":"Scrum Story Points are probably the single biggest stumbling block for team members. According to Scrum they shouldn’t be related to hours, but should be related to how easy or hard something is. Unfortunately, this concept can be challenging for many team members to grasp, so games like Planning Poker have to be adopted.\nAt its most basic, every one on the team gets a vote. They vote with cards they pull from their hand. Their hand is made up of Story Point cards for each valid set of points in the system being used. Everyone shows their cards. If the cards shown are all similar then the story is updated and folks move on. If the cards are different then debate is done and another vote is had. Continue until everything is scoped.\nIt is better than having no structure, but it doesn’t scale well. In many cases, much of the debate will center around what a “point” actually means. Older tickets often have to be rescoped as the session moves forward. We are cautious about using this technique.\nWhile not perfect we find that 3 Point Estimation is usually a better solution to estimation.","lastmodified":"2025-05-09T00:00:00-04:00","tags":null,"title":"Planning Poker"},"/radar/techniques/scrum":{"content":"Scrum is one of the more popular Agile Software Development techniques. Interestingly enough it just Waterfall Software Development rebranded. It is one of the few that you can purchase training and certification for, which should be your clue that Scrum really isn’t agile in the strictest sense. However, in comparison to other techniques like Six Sigma it is much lighter weight. There are some good points which you should try in whatever development process you use.\nThe good:\nSprint Planning Scrum Team Definition of Done / Acceptance Criteria Retrospective The bad:\nDaily Scrum / Standup Backlog Grooming Points Burn-down Charts The ugly:\nInfinite Backlog Sprints Moving Goal Posts / Crunch Culture “Full stack” Ceremonies Most of the good part of Scrum align the team’s focus with delivering value. Most of the bad and ugly parts are ceremonies, which folks oft confuse as value. This can lead to a death spiral of more formal process being used as a “fix” for a delivery problem.\nCare must be taken as following Scrum does not guarantee results. Instead Scrum should be treated like a collection of light weight techniques that can be applied or ignored as needed.","lastmodified":"2025-05-09T00:00:00-04:00","tags":null,"title":"Scrum"},"/radar/techniques/sre":{"content":"Site Reliability Engineer (SRE) is a very specific implementation of DevOps that Google uses. However, outside of Google, DevOps usually have a broader mandate. You have to decide if a narrowly focused SRE role is right for you.\nReference https://sre.google/ ","lastmodified":"2025-05-11T00:00:00-04:00","tags":null,"title":"SRE"},"/radar/techniques/test-pyramid":{"content":"Test Pyramid is a testing technique that places a higher emphasis on smaller, faster, and more isolated testing (i.e., unit testing). It is the testing equivalent of shift-left and results in better overall quality and faster shipment. It should be a key component of your testing strategy, if not the entire strategy.","lastmodified":"2025-03-29T00:00:00-04:00","tags":null,"title":"Test Pyramid"},"/radar/techniques/zanzibar":{"content":"Google Zanzibar is a white paper on how google handles fine-grained permissions authorization at scale. The first 2 sections of the document (Introduction & Model, Language, and API), and 1/2 of the third (Architecture and Implementation) are broadly applicable to anyone concerned about permissions. The rest of the document is really about Google’s scale, which is less universal. If you need to implement a permissions model then this is a good one.\nThe Permissions Schema Basically, the data schema is a simple tuple of <object>#<relation>@<user> that represents a relationship graph. The permissions are derived by asking if a certain relationship is explicit or implied by the graph. If yes, permission granted, if not then denied.\nThese tuples are stored in a database and evaluated dynamically at runtime. The creation of a tuple is effectively adding a permission, while removing one is like removing a permission.\n<object> is <namespace>:<id> but is basically anything that uniquely identifies something. This might be a virtual item like a report (report:5), or an abstract thing like a group (group:reporters), Basically anything, that isn’t a user.\n<user> is a <user id> (e.g., 10) or an additional relation (<object>#<relation>), thus creating a graph. A user can be human id (email) + context (usually device) or a machine (service account).\n<relation> is a string identifier for the type of relationship between object and user.\nSome ways relationships are mapped:\nUser 10 is an owner of doc:readme - doc:readme#owner@10 User 11 is a member of group:eng - group:eng#member@11 Members of group:eng are viewers of doc:readme - doc:readme#viewer@group:eng#member doc:readme is in a folder:A - doc:readme#parent@folder:A#… #… is self-referential relationship. Basically it is a means to draw a graph between two objects instead between an object and a user. Eval First, we - as the implementors - would define a finite set of relations. Then we would add check(user, object, <relation>) to our code. The <relation> would be the hard or soft-coded part in the code. The user would come from the user session, and the object would be from the request.\nChecking of rules is then a matter of graph traversal, if you are able to map between a user and an object then the check passes, if not then it fails. For example, if want to check(11, “doc:readme”, “viewer”) then the eval looks like this:\nDoes doc:readme#viewer@11 exist? No. Does doc:readme have any other relations that satisfy viewer? Yes, group:eng#member Does group:eng#member@11 exist? Yes. doc:readme#viewer@11 is true and can be cached. Defining a Namespace Schema A namespace is basically a rule set that dynamically implies relationships. Basically how to map relations.\nLet’s say you want to add a relationship “editor”. You previously had “owner” and “viewer”. Owner had edit but also had other abilities. To correctly the overly broad use of owner first you update the code and fix any use of check(..., \"owner\") to check(..., \"editor\") for any case where you care if they can edit. Then you update the namespace schema to rewrite / remap relations:\n1 2 3 4 5 6 7 8 9 10 11 relation { name: “editor”, userset_rewrite { union { child { _this {} } # all things explicitly given editor relation # Any that was explicitly given the owner relation is also an editor child { computed_userset { relation: “owner” } } } } } The above uses the _this which is the collection of all explicit tuples. Then it adds computed_userset which is a search pattern on the relationship owner. Effectively this means when someone checks for editor anyone that has the owner relation will return true as well. By doing this “editor” can be added having the same meaning as “owner” and then it can diverge as needed.\nDefining permissions Zanzibar is based on permissions so it is best to define fine-grained permissions vs coarse permissions like “admin”, “editor”, and “viewer”. For each object-type think about the permissions that make the most sense. For example for a bucket you’d likely want a separate permission for listing the content vs downloading a file.\nOnce the fine-grained permissions are defined it is time to update the code to use the fine-grained permissions. For example the service that serves GET /bucket/<name> would check for the <name>#list relation, while GET /bucket/<name>/<path> would check the <path>#download permission.\nThen we the namespace schema we can remap behaviors. Above for example makes owners also editors.\nRoles vs permissions Zanzibar is concerned about permissions, and evaluating those permissions. That is it. However, Roles and other higher level constructs are need by humans. They can be performed via relations, since it is a graph.\nThe recommended implementation is to define the fine-grain permission set in the namespace schema and check that in the code. Then in the app / service create a role or group style object.\nUse a tuple to bind a group to a relation on an object (e.g., doc:readme#viewer@group:eng#member).\nThen us another tuple to bind a user to a group (e.g., group:eng#member@11)\nGoogle Scale Internally Google uses a system that is implied by the whitepaper. But at their scale it would be impossible to check permissions using a vector check like explained in the paper. Instead they are reduce all permissions to their simplist form. Checks - for access - are executed against the flattened dataset. This reduces the complexity of the check and also increases the speed and accuracy.\nThe process that does this reduction works in a similar way to what is explained in the Prodspec and Annealing paper.\nNew Enemy problem and Zookies Note\nThis is a problem because google has to cache relations in order to meet SLAs.\nA New Enemy problem happens when a person that previously had access is continued to be allowed even if the permission is removed (usually due to caching). A zookie (Zanzibar Cookie) is a low cost means to mark oldest cache that can be used.\nSetup:\nLex has reader to plans A Kara has admin to plans A Kara need to write info that Lex cannot see to Plan A If Lex sees the secret info he will do bad things New Enemy Scenario:\nLex reads plans A (seeding the cache) Kara removes Lex’s access to plans A Kara adds secret information to plan A Lex reads plans A (cache returns true) so has access Lex does bad things With Zookies:\nLex reads plan A (seeds cache with Zookie T1) Kara removes Lex’s access to plan A (Plan A now has Zookie T2) Kara adds secret information to plan A Lex reads plan A Check sees cache has Zookie T1, but document has Zookie T2 Cache is expunged Full path is recalculated Lex is denied access Reference https://authzed.com/blog/what-is-zanzibar/","lastmodified":"2025-05-07T00:00:00-04:00","tags":null,"title":"Zanzibar"},"/radar/tools/capistrano":{"content":"Capistrano is a remote server automation and deployment tool written in Ruby.\nWhile this tool helped to usher in the practice of DevOps using Ruby as a DSL it was born before the time of Declarative IaC. And as with many Imperative IaC tools the IaC is error prone and becomes unmaintainable long term. It has long since been replaced better tools and should be abandoned if at possible.\nThings to use instead:\nAnsible Terraform Kubernetes ","lastmodified":"2025-01-05T00:00:00-05:00","tags":null,"title":"Capistrano"},"/radar/tools/container-structure-test":{"content":"Container Structure Test provides a powerful framework to validate the structure of a container image. These tests can be used to check the output of commands in an image, as well as verify metadata and contents of the filesystem.\nYour code has unit tests, right? So should your container. At a minimum you should have existence tests for the important files, like libraries that are needed, and even the main binary itself. You should have tests that important labels like the commit hash are there, any ports that opened by default and default ENVs exist.\nYou can even do things like run your binary to make sure it produces expected behaviors. Since in this day and age Containers are a must, so too is this tool.\nSmoke Tests\nThe goal with this tool is the basic smoke test and some very light usability testing. This is not an integration test suite. You really are just trying to make sure the container will at least run.","lastmodified":"2025-01-05T00:00:00-05:00","tags":null,"title":"Container Structure Test"},"/radar/tools/cue":{"content":"CUE is a schema validating tool that also does code expansion / generation. It is a different take on Policy-as-code and is made to provide generic guardrails in its own language format which can be translated into other ecosystem compliant formats like JSON Schema, Protobuf, or OpenAPI.\nWhile it is an interesting take on the problem, at this point in time, we don’t recommend it as there aren’t very compelling use cases. However, it might be just what you are looking for.\nWith all that being said, it is a good public demonstration of Google’s General Config Language (GCL) language that is used internally to configure much of what Google does. You can see that influence deeply inside the Design of CUE, so even if you don’t use CUE directly it is worth it a read.\nGoogle needed a way to write configurations that could be easily expanded and lightly validated. These configurations would be entirely private to Google, so there was no need to have a formal specification. In fact, separating the formal spec from validation and expansion would have been cumbersome. Their language defines a single format that can be used to generate and loosely validate configurations of any type that other systems needed, such as BorgCfn for Borg, JSON, XML, Protobuf, etc. The validations were not meant to be exhaustive, but rather to give programmers confidence that they were not making obvious mistakes. CUE is a public implementation of that idea.","lastmodified":"2025-07-10T00:00:00-04:00","tags":null,"title":"CUE"},"/radar/tools/docker-compose":{"content":"Docker Compose is a development tool for defining and running multi-container applications. It is included in the Docker ecosystem but 99% of all container deployments are going to be Kubernetes. Therefore, you want a tool that Kubernetes aware like Skaffold instead.","lastmodified":"2025-01-05T00:00:00-05:00","tags":null,"title":"Docker Compose"},"/radar/tools/helm":{"content":"Helm is the package manager for Kubernetes. And as a tool it is a critical tool in the Kubernetes ecosystem—it is essential. If you run a kubernetes cluster then you are very likely to eventually need to install a helm chart on your cluster.\nArtifactHub lists most public helm charts you may need.","lastmodified":"2025-07-13T00:00:00-04:00","tags":null,"title":"Helm"},"/radar/tools/k9s":{"content":"k9s is a terminal-based console for Kubernetes. It provides basic functionality, such as viewing the state of Kubernetes objects, and offers advanced features like resource filtering, log streaming, and namespace management. These capabilities make it an essential tool for efficiently managing Kubernetes clusters from the terminal.","lastmodified":"2025-05-19T00:00:00-04:00","tags":null,"title":"K9s"},"/radar/tools/pulumi":{"content":"Pulumi generates IaC based a programming language of your choice.\nAs with many Imperative IaC it suffers from serious drawbacks. However, for quick-hack lab environments - where the maintainer has little operational experience - it proves a compelling approach.\nWhile many similar tool are “hold” we leave this “assess” because pulumi uses terraform providers under the hood meaning that the actual actions taken and state maintained is that of Terraform. So this isn’t the worst stepping stone in the world of IaC.\nIf you are in a company that cannot or will not use Terraform and you have a mature enough team to actively avoid the pitfalls of Imperative IaC then this is a very competent solution. Otherwise steer clear because you are going to end up in a worst place than just building by hand.","lastmodified":"2025-01-05T00:00:00-05:00","tags":null,"title":"Pulumi"},"/radar/tools/skaffold":{"content":"Skaffold reports itself as “Fast. Repeatable. Simple.” And it is. Skaffold is to Kubernetes what Docker Compose is to Docker. However, it provides build, load, watch, rebuild loop directly on K8s clusters. This makes Skaffold an indispensable developer tool for any Shift Left org using Kubernetes.\nAs a developer tool for a certain type of org it is clearly categorized as “adopt”. However, despite Google’s insistence to the contrary, it is not a good Continuous Deployment tool. It will always need to be replaced eventually, but we find that the same mindset choose skaffold for CD has often taken other shortcuts that make migration harder than one would expect. Thus we are cautious.","lastmodified":"2025-01-05T00:00:00-05:00","tags":null,"title":"Skaffold"},"/radar/tools/terraform":{"content":"Terraform has been the defacto standard for IaC. However, it was knocked out of its “Adopt” classification in the Tech Radar due to the commercial license change. This means that it has become too cost-prohibitive for some companies, which have invested in other solutions.\nAt the time of this writing OpenTofu – an open-source fork of Terraform created before the license change to maintain a free and community-driven alternative – is a compelling option. However, it will diverge eventually; making choice confounding. If you can use Terraform, do so. Otherwise, there are plenty of good alternatives.\nA later announcement by IBM on the purchase of Hashicorp made the license change understandable. IBM does have a long history of supporting the open source community, but they are a commercial entity. Therefore, we remain cautious about Terraform’s future as the de facto standard.","lastmodified":"2025-01-05T00:00:00-05:00","tags":null,"title":"Terraform"},"/radar/tools/wireguard":{"content":"Wireguard bills itself as a fast, modern VPN solution. It does this by limiting interoperability to only itself, and tracking improvements via software updates. Each client and server can only be a certain version skew from each other or communication will fail. If you are in control of both ends of the communication and are in a place where you can keep the VPN software current then Wireguard is a viable solution.\nWe are glad it exists. However, it is not a wide use case.","lastmodified":"2025-04-23T00:00:00-04:00","tags":null,"title":"Wireguard"},"/resources/bash-tips-and-tricks":{"content":"The following are common useful tricks for Bash programming.\nDirectory Containing the Script Many times it can be useful to get the directory of where the script lives even if PWD is set to something else.\n1 2 3 APP_DIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )/.\" && pwd )\" source \"$APP_DIR/../lib/mylib.sh\" Iterate over lines in a file Warning\nThe file will need an empty line at the end or you will miss the last line.\n1 2 3 while IFS= read -r line; do echo \"$line\" done < file Iterate over command output Very similar to files command output can be treated like a file using the <() operator. This for example can be used to grep certain strings out, and or manipulate the line ahead of iterating.\n1 2 3 while IFS= read -r line; do echo \"$line\" done < <(ls .) Local OR and AND With the test ([) command\n1 2 3 4 5 6 7 if [ \"$a\" == 1 -a \"$b\" == 1 ]; then echo \"a AND b\" fi if [ \"$a\" == 1 -o \"$b\" == 1 ]; then echo \"a OR b\" fi With the build-in [[\n1 2 3 4 5 6 7 if [[ \"$a\" == 1 && \"$b\" == 1 ]]; then echo \"a AND b\" fi if [[ \"$a\" == 1 || \"$b\" == 1 ]]; then echo \"a OR b\" fi Logging Very often logging is useful, especially in bash. However, many times logging clobbers proper output. The best solution is to use standard error for logging. Also, it is usually useful to have levels of logging, so you put logging lines in your code that are unused by default.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 function now() { TZ=\"UTC\" date +\"%Y%m%d%H%M%S\" } function log() { echo \"$@\" >&2 } function debug() { if [[ -n \"$DEBUG\" ]]; then log \"$(now) DEBUG:\" \"$@\" fi } function info() { log \"$(now) INFO:\" \"$@\" } function warn() { log \"$(now) WARN:\" \"$@\" } function error() { log \"$(now) ERR:\" \"$@\" } function fatal() { log \"$(now) FATAL:\" \"$@\" exit 1 } ## Usage debug \"This is a debug\" info \"this is an info message\" References Bash-hackers wiki (bash-hackers.org) Shell vars (bash-hackers.org) Bash Guide (mywiki.wooledge.org) ","lastmodified":"2025-04-18T00:00:00-04:00","tags":null,"title":"Bash Tips and Tricks"},"/resources/container-tips-and-tricks":{"content":"The following are useful tricks for containers.\nTools Containers It is often useful to put tools like gcloud, aws, and others in containers and use the containers in place of installing programs directly on the computer. This makes the commands more portable, but also make it easier to upgrade and rebuild should something go wrong.\nFirst, install docker-desktop for your give OS, or some other container runtime.\nSecond, create a script like the following and put it somewhere it can be loaded.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #!/bin/bash APP_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\" source \"$APP_DIR/lib/log.sh\" # docker_run does some basic stuff to to setup a # `docker run <standard args>` so that the rest of the args # can be blindly passed in. function docker_run() { local args=(\"--rm\") # Make a local array of args # If standard out/in is a terminal if [[ -t 0 ]]; then args[\"${#args[@]}\"]=\"--interactive\" args[\"${#args[@]}\"]=\"--tty\" else info \"No input\" fi debug \"Args:\" \"${args[@]}\" # Note: if you use a different docker runtime (like podman) # then simply switch the args and the command here. docker run \"${args[@]}\" \"$@\" } Third, create a wrapper for the function you want. For example, the AWS CLI.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #!/bin/bash # Trick from the bash tips and tricks page APP_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\" # Load the docker script from above source \"$APP_DIR/lib/docker.sh\" # this is the container name you will be building IMAGE_TAG=\"aws-cli\" # Setup the local directories that get mounted # That way the auth state gets persisted between # runs of the container SRC_PATH=/workdir mkdir -p \"$HOME/.aws\" mkdir -p \"$HOME/.kube\" MOUNT_PATHS=( -v \"$APP_DIR:$SRC_PATH\" -v \"$HOME/.kube:/root/.kube\" -v \"$HOME/.aws:/root/.aws\" ) # Now pass it all to docker_run docker_run \"${MOUNT_PATHS[@]}\" --workdir \"$SRC_PATH\" --entrypoint=\"/usr/local/bin/aws\" \"$IMAGE_TAG\" \"$@\" Finally, build / cache the needed CLI tool as a local container.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # Often there exists a public container with the tool FROM amazon/aws-cli:latest # Often a good idea to label so you know you built it, and when LABEL Maintainer=\"Myself\" LABEL Revision=\"20211008150705\" # Now install any other tools that tools that might help when using # this container. # # kubectl allows for the connection to AWS EKS clusters # JQ is a JSON CLI parser to help with AWS API output. # YQ is the same as JQ but for YAML, so works nicer with # K8s objects. ENV KUBECTL_VERSION=1.19.6 ENV JQ_VERSION=jq-1.6 ENV YQ_VERSION=v4.7.1 # hadolint ignore=DL3020 ADD \"https://amazon-eks.s3.us-west-2.amazonaws.com/${KUBECTL_VERSION}/2021-01-05/bin/linux/amd64/kubectl\" /usr/local/bin/kubectl # hadolint ignore=DL3020 ADD \"https://github.com/stedolan/jq/releases/download/${JQ_VERSION}/jq-linux64\" /usr/local/bin/jq # hadolint ignore=DL3020 ADD \"https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64\" /usr/bin/yq RUN chmod +x /usr/local/bin/* RUN yum install -y tar-1.* && yum clean all ENTRYPOINT [\"/bin/bash\"] For bonus points you can setup a nightly CI pipeline to build containers like this and push to public or private image repo. But I leave that as an exercise for the audience.","lastmodified":"2025-04-18T00:00:00-04:00","tags":null,"title":"Container Tips and Tricks"},"/resources/git-tips-and-tricks":{"content":"The following are useful git tips and tricks.\nCleanup to a given branch Often times a git repo needs to be brought up-to-date with the remote quickly. Using a feature branch style also means that branches need to be cleaned up as well. The following does all that.\nInfo\nIf you copy the below into git-cleanup and put it in your path, then Git will automatically consider it a subcommand. So within a git repo just run git cleanup main (note the space) will move your to the main branch, sync it with the remote, and delete any synced branches.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #!/bin/bash branch=\"$1\" if [[ -z \"$1\" ]]; then echo \"No branch supplied, too dangerious to assume\" exit 1 fi set -u git fetch -p git checkout \"$branch\" git pull # shellcheck disable=SC2063 # we are looking for \"*\" while IFS= read -r line; do git branch -d \"$line\" || echo \"Unable to delete '$line', ignoring\" done < <(git branch | grep -v \"^*\" | awk '{print $1}' ) # Note: the line starting \"*\" is the current branch, ignore it # try to safely delete the rest Find the hash of the latest commit 1 2 3 4 5 # If you know you are in the HEAD commit git rev-parse HEAD # or, one that works regardless git log --pretty=\"oneline\" | head -n1 | awk '{print $1}' Find the files that changed 💡 This works if you have a stable ancestor branch. 1 2 3 4 5 6 7 8 9 10 11 12 ANCESTOR=\"master\" COMMMIT=\"HEAD\" # replace with commit hash if you could be in detached head # sync the acenstor branche to ensure it has full history git fetch origin \"$ANCESTOR:remotes/origin/$ANCESTOR\" # diff to the anscestor git diff --name-only \"origin/$ACTIVE_BRANCH\"...\"$COMMIT\" # Unfortunately this doesn't catch if you are on $ACTIVE_BRANCH. # So you need to check the the last commit if the above produces no output git diff --name-only \"$COMMIT^..$COMMIT\" ","lastmodified":"2025-04-19T00:00:00-04:00","tags":null,"title":"Git Tips and Tricks"},"/resources/golang-tips-and-tricks":{"content":"The following are some basics on GoLang.\nHow to justforfunc: Programming in Go - YouTube GitHub - campoy/justforfunc: The repository for the YouTube series JustForFunc GitHub - campoy/go-tooling-workshop: A workshop covering all the tools gophers use in their day to day life Documentation - The Go Programming Language pkg.go.dev - Documentation on most go packages Go Docker image golang-standards · GitHub - Shows an example project layout and project-templatez Quick start Install Go: brew install go\nYou may want to use docker which is what build systems will use\n1 2 docker pull golang:1.15.4 docker run -rm -v \"$PWD\":/code -w \"/code\" golang:1.15.4 go build -v Install Go plugin to your favorite Editor (e.g., go-plus for Atom)\nSetup your repo\nIf a library then it should be in $GOPATH/src/<url path>\nGeneral project layout GitHub - golang-standards/project-layout: Standard Go Project Layout\nUniversal files:\nREADME.md - Every project should have one LICENSE.md - Every project should have one run - A script to easily build or test the code go.mod - initialize as a module go mod init <name> go.sum - commit this if it exists. /cmd/<name> This is the place for the main package. It should build into an executable. <name> should be the name of the command you want to build. You will build it via go build cmd/<name>.\nNot much code should be here. Just a means to load stuff from /internal, or /pkg.\n/internal Private code should be here. This is enforced by the compiler\n/pkg Library code that is ok for others to use.\n/vendor (managed) This directory is (or should be) managed by your dependency manager and not by you directly.\n/build Not universally recommended, but if your build system takes scripts then it should be /build/ci and then linked to whatever location the build system wants it to actually be. Where possible it is just a thing wrapper around run.\nTools go - The general tool for go devel - Go debugger GitHub - tsliwowicz/go-wrk: go-wrk - a HTTP benchmarking tool based in spirit on the excellent wrk tool goimports - An improved fmt tool that also handls imports Libraries https://awesome-go.com/ - A curated list of awesome Go libraries https://threedots.tech/post/list-of-recommended-libraries/ - 22 libraries known to work in production Command Tools GitHub - spf13/cobra: A Commander for modern Go CLI interactions\nConfiguration Loading GitHub - spf13/viper: Go configuration with fangs Database Examples:\nconcourse/atc/db - Does not use an ORM or Migration library, example of how to do it manually DB Drivers https://golang.org/pkg/database/sql/ - Golang’s built in SQL Library, most have a compatable interface https://github.com/lib/pq - database/sql Postgres Driver https://github.com/jackc/pgx - Postgres specific driver as well as database/sql compatible driver. The specific driver interface is more performant, but the standard interface is available for use with other libraries. https://github.com/Masterminds/squirrel - Fluent SQL generation for golang makes queries easier. DB ORMs https://gorm.io - A Rails-type ORM similar to ActiveRecord. https://github.com/go-pg/pg - Golang ORM with focus on PostgreSQL features and performance https://godoc.org/github.com/Masterminds/structable - Simple Struct to DB mapper / recorder DB Migrations https://github.com/golang-migrate/migrate - Database migrations. CLI and Golang library. This is library agnostic. It has a BinData source driver that allows SQL migration files to be loaded directly into binary. https://gorm.io - A Rails-type ORM similar to ActiveRecord, with a similar migration style WARNING: in practice these style migration are fragile as there is a tight coupling to the record. Git https://pkg.go.dev/github.com/go-git/go-git/v5 - A pure Golang implementation of Git\nGerrit Note\nAs a Google maintained project all Golang code is maintained in public version of their GitOnBorg system which uses Gerrit as the review system. To contribute to the main golang code base you have to understand Gerrit.\nGerrit is the code review system used by Google’s Public GoLang team.\nChanges (cls) are created by pushing to a special branch called refs/for/<target branch>. This opens the Equivalent of a GitHub PR. With the difference being that in Gerrit a commit is the unit of work, and in GitHub the branch is.\nTopics are a way of associating changes across multiple repos. It is interfaced by using the config push.pushOption = topic=<name>, or doing it via an option during the push: git push ... -o topic=<name>, or git push origin HEAD:refs/for/main%topic=<name>.\nhttps://github.com/andygrunwald/go-gerrit - Gerrit API client\nGraphs https://github.com/dominikbraun/graph - Generic graph data-structure HTTP http - The Go Programming Language\nGitHub - justinas/alice: Painless middleware chaining for Go - Allows Middlewares\nGitHub - throttled/throttled: Package throttled implements rate limiting access to resources such as HTTP endpoints.\nGitHub - justinas/nosurf: CSRF protection middleware for Go.\nHTTP Router / MUX GitHub - vmihailenco/treemux: Fast and flexible HTTP router Logging log - The logging package is enough if all you want to do is add a date Structured Logging In most cases structured logging is what you want.\nslog - slog augments log by adding levels, and fields. It is usually enough.\nGitHub - sirupsen/logrus: Structured, pluggable logging for Go. - Dead project as of 2020-11\nGitHub - rs/zerolog: Zero Allocation JSON Logger\nGitHub - uber-go/zap: Blazing fast, structured, leveled logging in Go.\nGitHub - apex/log: Structured logging package for Go.\nMerging Objects GitHub - imdario/mergo: Mergo: merging Go structs and maps since 2013.\nMetrics (prometheus) prometheus · pkg.go.dev\nParsing YAML https://gopkg.in/yaml.v2 - Built-in yaml.v2 sigs.k8s.io/yaml - YAML parser from K8s / Helm Use this one when YAML and JSON inter-opt is needed. 1 2 3 4 5 6 7 // Reading unstructured yaml data := map[string]interface{}{} bytes, err := yaml.Parse(fileBytes,&data) if err != nil { return nil, err } Static Files GitHub - markbates/pkger: Embed static files in Go binaries (replacement for gobuffalo/packr)\nTesting BDD https://github.com/onsi/ginkgo - BDD library https://github.com/onsi/gomega - Ginkgo’s Preferred Matcher Library TDD https://github.com/smartystreets/goconvey - A nicer testing framework, more TDD like","lastmodified":"2025-04-18T00:00:00-04:00","tags":null,"title":"Golang Tips and Tricks"},"/resources/hyrums-law":{"content":"An observation on Software Engineering\nPut succinctly, the observation is this:\nWith a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviors of your system will be depended on by somebody.\nhttps://www.hyrumslaw.com/","lastmodified":"2025-04-18T00:00:00-04:00","tags":null,"title":"Hyrums Law"}}