{"/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 idea is 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":"2026-05-16T00:00:00-04:00","tags":null,"title":"Hugo Wikilinks"},"/blog/20250418-kustomize-rollout":{"content":"[Kustomize](/garden/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 deployment. However, this can cause issues when you need to fix your base, as it will happily update all your overlaid environments en masse; which is less than 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 a little more annoying if you have to exclude or remove 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 objects 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](/garden/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 kustomization.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 explicit 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":"2026-05-16T00: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 affect what deployment options you have. One of the easier ways - from the server perspective - is websockets. However, not all load balancers or WAFs support 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 endpoint via an Ingress through a high performance IngressController and you should be all set.","lastmodified":"2026-05-16T00: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 changes to IaC lines 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 affect the underlying infrastructure. If the IaC tool doesn’t error, that is a 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 failure 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 relatively small changes in actual code cause large 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 than 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 through a standardized process. Environments will tend to drift both making future merging difficult. ","lastmodified":"2026-05-16T00:00:00-04:00","tags":null,"title":"Blast Radius - Critical Context"},"/blog/20260603-ai-proof-software-careers":{"content":"A lot of developers feel career anxiety about AI. Some of that is hype. Some is backed by data. The backed part is often contradictory: big productivity gains in one study, a slowdown in another, softer junior hiring in payroll data.\nHere we talk about what the data actually means and what you can do about it.\nThe Contradictions Headlines on AI and developer careers often contradict each other. That is not a reason to ignore the data. It is a reason to read carefully.\nProductivity Is Real, but the Number Depends on the Study Conclusion: AI helps on many tasks. Throughput metrics and self-reported speed do not tell the whole story. One headline percentage is a weak basis for a career plan.\nMIT field RCTs, 2024: about 26% more completed tasks with Copilot across roughly 4,900 developers. GitHub lab study, 2022: 55% faster on a bounded coding task. Jellyfish working paper: about 8.7% faster task completion at adopting firms; quality measures did not decline. NAV IT longitudinal study, 2025: no statistically significant change in commit metrics after Copilot rollout. The Labor Market Is Squeezing the Bottom Rung Conclusion: This is not “no software jobs.” The backed worry is fewer classic junior slots for work models can draft quickly.\nStanford Digital Economy Lab, 2025: workers aged 22-25 in AI-exposed occupations saw roughly 16% relative employment decline vs less-exposed roles. Same study, software developers: employment for that cohort fell nearly 20% from its late-2022 peak by September 2025. Experienced workers in the same occupations did not show the same pattern. Experienced Work Does Not Automatically Get Faster Conclusion: Speed is not the same as correctness, operability, and accountability. That gap is where durable careers still live.\nMETR RCT, 2025: experienced open-source contributors took 19% longer with AI allowed; many still felt faster. See also a critique of the study design. Stack Overflow Developer Survey, 2025: 46% actively distrust AI accuracy; 66% frustrated by “almost right” output. What It Means The backed data points to uneven tools, pressure on junior hiring, and persistent need for human judgment. That maps to two practical views: what tends to hold up and what is most exposed.\nWhat Holds Up Better No title comes with a guarantee. The studies do not rank job titles. They do suggest where humans are still needed:\nProduction ownership. Site reliability, incident command, and on-call culture exist because outages have cost, blameless learning, and a named owner. AI can suggest runbook steps. It does not carry the pager or explain a missed SLO to leadership. Security and adversarial thinking. Threat modeling, incident response, application security, and offensive research assume an opponent who adapts. Generated code can be wrong in subtle ways. Trust boundaries and “did we actually fix prod?” need skeptical humans. Platform and infrastructure at org scale. Identity, networking, cost, compliance, and migration history are specific to your employer. Models rarely have your full graph. The engineer who can reason across that graph stays relevant. Regulated and high-stakes domains. Healthcare, finance, defense, and safety-related embedded systems add liability, audits, and sign-off workflows. Tools accelerate drafts. Humans still sign. Architecture and technical leadership. Tradeoffs across teams, legacy, and multi-year bets are political and technical. Models propose options. They do not own the decision when the bet fails. Customer-trusted technical roles. Solutions architecture, forward-deployed engineering, and staff roles on revenue-critical accounts combine judgment, communication, and delivery under scrutiny. Building and operating AI systems. Evaluation, guardrails, observability, cost control, data pipelines, and human review loops are busy fields. Adoption ran ahead of trust. Domain depth plus shipping. The durable builder is often “engineer plus billing, logistics, clinical workflow, or security operations,” not a generic feature author. If your résumé already shows multi-stakeholder delivery and production cutover risk, you may already be closer to these patterns than the anxiety headlines suggest.\nWhat Is Most Exposed Some of the career anxiety maps here. These roles lean on throughput without ownership:\nSpec-driven UI and API work with no system context Thin integration glue with no failure-mode analysis Roles measured by merges, not by prod outcomes Documentation or boilerplate factories disconnected from operations Models are strongest on spec-and-output work with little system context. They still do not own a bad deploy, a security incident, or a wrong architectural call across three teams.\nWhat to Do About It The studies conflict. Our read is that productivity gains are real but uneven; junior hiring is under pressure; experienced work still needs human judgment and trust.\n“AI-proof” is the wrong goal. Accountability-proof is closer to the truth. While there are no guarantees, the following are our recommendations:\nOwn outcomes, not keystrokes. Measure success by latency, reliability, cost, security posture, and customer results, not lines and commits. Tie your work to prod or business risk. Verify before you trust. Treat model output like a fast junior: useful, needs review. Specify intent in verifiable slices. Use tests and static analysis. Stage carefully. Reject the first answer when it does not hold up. Think in systems, not syntax. Syntax is table stakes. Build skill in data flow, failure modes, boundaries, operability, and evolution across services you did not write. Learn AI orchestration. This is not prompt trivia. Assemble context from repos, runbooks, tickets, and metrics. Decompose work. Parallelize where safe. Gate risky changes. Write down why you chose a path. Go deep in one scarce lane. Pick security, data, distributed systems, a regulated industry, hardware-adjacent work, or AI infra and eval. Shallow feature-only generalists face the most hiring pressure in the data. Invest in communication and trust. Write clear RFCs. State tradeoffs honestly. Be willing to be the person called when production is on fire. Retool without panic. Tooling will churn. Adopt workflows on real projects. Measure speed and defect rate. Keep what works. Neither “ignore AI” nor “AI replaced me” is a strategy. A Simple Mental Model flowchart TB A([Problem and context]) --> B[[Human: scope, risk, constraints]] B --> C{{AI: draft, explore, automate}} C --> D[[Human: verify, integrate, operate]] D --> E([Outcome in production])The durable career is the human + AI. As the human, you scope risk and constraints, then verify and operate in production. Let AI draft and explore in the middle.\nYour response is not to code less. It is to ship correct, valuable systems with AI as labor, and to lean on taste, verification, and trust when the model is only almost right.","lastmodified":"2026-06-03T00:00:00-04:00","tags":null,"title":"What Software Jobs Survive AI, and What You Can Do"},"/blog/20260605-agent-skills":{"content":"I have been using AI for many years, and within the last year really got heavy into MCP and Agent Skills Framework. At this point it is clear that using agents is now a required skill by any engineer and many (if not most) non-engineering jobs.\nThe thing that is going to separate the haves and have-nots in this new reality is the curated list of personal Agent skills you develop and take with you. Here I am going to explain how to do that successfully.\nFirst, I am going to assume you are familiar with AI Agents, and how your particular agent framework loads skills. There are many ways this is done but at the end of the day it boils down to a directory that contains an AGENTS.md (or CLAUDE.md because Anthropic uses that name) and a skills directory of directories containing SKILL.md files. That directory is the one you need to manage and maintain.\nA Soul The first thing you need to do is give your Agent a Soul. The SOUL.md gives your agent a purpose, and rules for how it behaves with you. Start small and define these things:\nYour Identity - How do you think of yourself (i.e. “I am …”) Its Identity - How should it know itself (does it have a name? Should it be an employee of yours? Should it pretend to be you?) Communication Style - How you like to be talked to (i.e., “Concise with a lot of links backing up your statements”) Operating Principles - What MUST the agent do, what MUST it NOT do First: Tool-Skills and MCPs The very next thing is to load a few tool-based skills. These tell the agent how it can do things. I keep a fairly up to date curated list of Agent Skills to pull from. First you should look for tool-skills which are basic and portable skills like apple-reminders.\nAdd one or two you think will be useful, and run them to see if there is additional setup that is needed (usually auth tokens and MCP servers). Once you have a basic conversation and get the agent to do a thing it is time to open the floodgates, and let the agent maintain itself.\nUnderstand Skill Types There are no hard and fast rules with skills (because there is no standard). However, I like to think about skills as existing in one of three camps. They are:\nCapability Skills - These teach the Agent to do a thing (like interface with apple reminders). They are highly portable, and can often be useful to others. Name these for what they do (i.e., apple-reminders, proofread) Behavioral Skills - These teach the agent how to behave when using other skills (like using GTD when managing reminders). Name these for what they layer on top of (i.e., reminders-gtd, proofread-blog) Domain Skills - These are outcome based skills that you define that reference the other skill types. These are highly specific to you. Name these for what they do (i.e., morning-briefing, company-research) Skillify Skill The first real skill you need is the skill that can maintain skills for you. As meta as it sounds it is straightforward to create this skill via a conversation with the agent.\nCreate the stub skill as follows:\n1 2 3 4 5 --- name: skillify description: \"Create, improve, and audit SKILL.md. Use when building a new skill from scratch, refining an existing skill's description or commands, or auditing all skills for quality and staleness.\" --- # Skillify Then ask the agent to create this skill based on the description. Or even using <path to my list of skills repos> create this skill.. Just continue until you have something that looks like it should work. Then use it.\nCreate Your Own soul-audit Skill Now comes the first in a long line of skills you’ll create: the soul-audit. Your agent’s SOUL is critical to your success. So you need a skill that can specifically manage it; and it is a great test of your skillify skill.\n1 /skillify create soul-audit. I want to periodically update your soul (AGENTS.md) through a series of prompts. It should be re-runnable anytime to update any section of the soul. Use when I specifically ask for a soul-audit to be performed. Your skill will be specific to you. But I like to keep my sections separate so it is easier for each phase to only update its section. My AGENTS.md looks like this:\n1 2 3 4 5 6 7 # AGENTS.md Load the following in order: - SOUL.md - USER.md - IDENTITY.md - ACCESS_POLICY.md Then my skill looks something like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # SOUL Audit Generate the agent's identity and operational configuration through an interactive interview. Each phase produces a file. Any phase can be re-run independently to update. **IMPORTANT:** This skill generates content from the USER'S OWN ANSWERS. It NEVER ships pre-filled content. ## Phases ### Phase 1: Identity Interview Ask: \"What is this agent to you? Research partner? Executive assistant? Thinking partner? All of the above?\" Generate: SOUL.md identity section. ### Phase 2: Vibe Calibration Show 3-4 communication style examples: - **Formal:** \"I've prepared a comprehensive analysis of the situation...\" - **Direct:** \"Here's what's happening. Three things matter.\" - **Technical:** \"The root cause is in the connection pooling. Here's the fix.\" - **Casual:** \"Yeah so basically the thing is broken because X. Easy fix.\" Ask which feels right. Generate: SOUL.md vibe + communication style sections. ... Once it is there, run it via /soul-audit.\nConclusion In our new AI-driven world your critical skill is being able to maintain a curated list of Agent Skills that is unique to how you work. This MUST be continually updated and maintained, so the first set of skills helps you do that. From there build out as you need to.","lastmodified":"2026-06-05T00:00:00-04:00","tags":null,"title":"Curating Personal Agent Skills"},"/contentindex/index.json":{"content":"","lastmodified":"0001-01-01T00:00:00Z","tags":null,"title":"Content Index"},"/garden/12-factor-app":{"content":"12 Factor App\nThe 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.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"12 Factor App"},"/garden/3-point-estimate":{"content":"3 Point Estimate\nThe 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 possibly take more than…” 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 a spreadsheet, this is pretty easy to do.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"3 Point Estimate"},"/garden/a-model-of-communication":{"content":"A Model of Communication is a framework for matching communication medium to message so noise in the channel does not swamp intent. We assess it under Specification as a reference model for when async, sync, rich, or lean channels fit—not as a product to deploy.\nBlurb A model for Computer Mediated Communication in Online Dispute Resolution.\nSummary Ever wonder why social media devolves into chaos? Ever wonder how some managers insist on in-office work while others are fully remote?\nNo matter how well-crafted the message, there is noise in every transmission that degrades results. This model provides a framework for ensuring the correct communication tools are used at the correct times; matching medium to message reduces noise and improves outcomes.\nDetails The source site is worth a full read.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-19T00:00:00-04:00","tags":null,"title":"A Model of Communication"},"/garden/access-on-demand":{"content":"Access on Demand\nAccess 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 pattern 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 request aod groups for a set duration. The reason given is logged. When the request is made a new session token is provided with the new group added, and an expiry timestamp no longer than 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. ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Access on Demand"},"/garden/agent-client-protocol":{"content":"The agent client protocol allows separate systems to use an agent as a service in its own right. This allows any client (like an IDE) to use the agent in place of an LLM. This has the added benefit of allowing the agent to have Agent Skills and persistent storage.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Agent Client Protocol"},"/garden/agent-skills-framework":{"content":"The Agent Skills format packages repeatable agent know-how as a folder with a required SKILL.md (YAML frontmatter plus markdown instructions). We assess it: portable across Claude Code, Gemini, Cursor, Codex, and other runtimes, but tool-specific extensions and discovery quality still vary. Use it when you want skills you can carry between agents. Pair with Agent Skills - Sources for vetted repos.\nBlurb Skills are reusable, discoverable modules that package instructions, scripts, and resources for AI agents. A skill is a directory with a required SKILL.md file and optional bundled resources. Skills use progressive disclosure: metadata stays in context; the body loads when the skill triggers; bundled files load on demand.\nSummary When Action Building portable ops for coding agents Adopt the layout: one folder per skill, SKILL.md with strong name and description One-off prompts in a single tool Skip; inline rules or tool-native config may be enough Importing community skills Assess each repo; use Agent Skills - Sources; run in sandbox first Layout: skills/<skill-name>/SKILL.md plus optional scripts/, references/, assets/. The description field is the primary trigger signal. Keep the body under ~500 lines. Push detail into reference files.\nIn this vault: agent-skills/skills/ mirrors the pattern. gbrain two-layer pages and identity files in AGENTS.md / CLAUDE.md sit alongside skills.\nDetails Required frontmatter 1 2 3 4 --- name: my-skill # lowercase, hyphens; matches folder name description: \"What it does and when to use it (trigger keywords live here).\" --- Skill types (informal) Type Purpose Example Capability Teach a concrete tool or API apple-reminders, proofread Behavioral Layer policy on other skills reminders-gtd, proofread-blog Domain Your outcome-specific workflow morning-briefing, company-research Runtimes in the garden Claude Code: .claude/skills/ or shared agent-skills/ Cursor / cursor-agent: .cursor/skills/ Gemini CLI, Codex, Cline, OpenCode: tool-specific paths; core SKILL.md often ports with path changes only See Agent Skills - Sources for official docs, awesome lists, and marketplaces.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-06-05T00:00:00-04:00","tags":null,"title":"Agent Skills Framework"},"/garden/agent-skills-sources":{"content":"A living research document tracking good sources of agent skills to draw from. Covers Claude Code, Gemini CLI, and cross-ecosystem skill/prompt repositories.\nMaintenance: Review quarterly. Update “last checked” dates when vetting a source. Remove dead or low-quality links.\nBlurb The SKILL.md format is an open standard for teaching thinking AI models specific, repeatable skills. A growing ecosystem of community repositories, marketplaces, and cross-agent collections makes skills portable across Claude Code, Gemini CLI, Cursor, Codex, and others.\nOfficial Sources Source URL Notes Anthropic Skills Docs https://code.claude.com/docs/en/skills Canonical SKILL.md format spec anthropics/skills (GitHub) https://github.com/anthropics/skills Anthropic’s own first-party skills; reference implementations Claude Code issue tracker https://github.com/anthropics/claude-code/issues Community bug reports often reveal undocumented skill patterns Curated “Awesome” Lists Aggregator repos and sites , good for broad discovery.\nSource Notes Last Checked hesreallyhim/awesome-claude-code Skills, hooks, slash-commands, orchestrators, plugins. Most comprehensive single list. 2026-05-06 travisvn/awesome-claude-skills Focused on SKILL.md-format skills; ~8.7k stars 2026-05-06 ComposioHQ/awesome-claude-skills 1000+ production-ready skills across Claude.ai, Claude Code, and coding agents 2026-05-06 rohitg00/awesome-claude-code-toolkit 135 agents, 35 skills, 42 commands, 176+ plugins, 20 hooks, 15 rules 2026-05-06 Individual Collections Maintained by practitioners , often higher signal-to-noise than aggregators.\nSource URL Notes Last Checked wshobson/commands https://github.com/wshobson/commands Production-ready slash commands; good engineering workflow patterns 2026-05-06 glebis/claude-skills https://github.com/glebis/claude-skills Personal collection; broad use-cases 2026-05-06 alirezarezvani/claude-skills https://github.com/alirezarezvani/claude-skills 232+ skills across Claude Code, Codex, Gemini CLI, Cursor 2026-05-06 garrytan/gstack https://github.com/garrytan/gstack gbrain author’s exact Claude Code setup; 23 opinionated tools spanning CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA roles 2026-05-06 Marketplaces & Directories Source URL Notes SkillsMP https://skillsmp.com/ Marketplace for agent skills across Claude, Codex, ChatGPT Toolradar blog https://toolradar.com/blog/best-claude-code-skills-2026 Curated top-20 list; good for quick survey Firecrawl blog https://www.firecrawl.dev/blog/best-claude-code-skills Editorial picks; refresh periodically ScriptByAI resource list https://www.scriptbyai.com/claude-code-resource-list/ Broad 2026 resource compilation MCP Servers (Complements Skills) MCP servers extend what tools Claude can call; skills extend how Claude reasons. Both compound.\nSource URL Notes Last Checked modelcontextprotocol/servers https://github.com/modelcontextprotocol/servers Official reference implementations 2026-05-06 wong2/awesome-mcp-servers https://github.com/wong2/awesome-mcp-servers Most-linked community list 2026-05-06 mcp-awesome.com https://mcp-awesome.com/ 1200+ quality-verified servers, searchable directory 2026-05-06 abordage/awesome-mcp https://github.com/abordage/awesome-mcp Auto-updated daily; good for freshness 2026-05-06 Cross-Ecosystem Inspiration Not SKILL.md format, but good sources for ideas , patterns that adapt directly.\nSource Notes Cursor rules repos (awesome-cursorrules) .cursorrules files are functionally similar to SKILL.md; large existing corpus GitHub Copilot custom instructions Per-repo instructions that shape behavior , same pattern, different runtime ChatGPT custom instructions / GPT Actions Prompt patterns for persona, workflow, and tool-use OpenAI Codex CLI plugins SKILL.md format is cross-compatible; Codex skills often translate directly Blogs & Articles Source URL Notes DEV Community - Claude Code Skills Practical Guide https://dev.to/muhammad_moeed/claude-code-skills-a-practical-guide-for-2026-3f6p Hands-on walkthrough Medium - 10 Must-Have Skills https://medium.com/@unicodeveloper/10-must-have-skills-for-claude-and-any-coding-agent-in-2026-b5451b013051 Quick survey of high-value skills Evaluation Criteria When vetting a new source:\nFormat compliance; Is it SKILL.md format, or requires adaptation? Cross-agent portability; Works only in Claude Code, or also Gemini CLI / Cursor / Hermes? Maintenance signal; Last commit date, open issues, star trajectory. Domain fit (DevSecOps, PKM, automation) relevant to Kamenik Solutions or personal use? Quality bar; Are skills self-contained with a clear trigger description in frontmatter? Open Threads Survey hesreallyhim/awesome-claude-code for DevSecOps and PKM-relevant skills to import See Also Agent Skills, framework overview gbrain, two-layer page convention used by this document hermes-agent, local agent runtime Claude Code Gemini CLI ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Agent Skills - Sources"},"/garden/agile-software-development":{"content":"Agile Software Development\nIn 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.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Agile Software Development"},"/garden/ai-agent":{"content":"Under Tool, AI Agent covers runnable agent tools, services that run an agent loop (observe context → plan → call tools → persist state) rather than one-shot chat completions. You install or self-host them and build workflows on top, often via Agent Skills and protocols like Agent Client Protocol.\nIn this subcategory: repo-bounded coding agents such as cursor-agent, Claude Code, Codex, Gemini, and OpenCode (terminal/IDE surfaces are how you run them; the product is the agent loop, not the editor distribution). Not here: raw model runtimes like Ollama (Platform); editor shells without an agent runtime under IDE; broad Artificial Intelligence & Machine Learning techniques.\nGarden stance: omnichannel personal agents (Hermes, OpenClaw) are hold / Moved Out; hard to secure across machines (ADR-0003-each-machine-runs-pipelines-independently). Adopt cursor-agent (with Cursor) for daily work; other bounded coding agents are trial when another vendor or open-source hub fits better.\nTag an item here when the core value is an agent with tools, sessions, and autonomous or semi-autonomous action; not just an editor without an agent runtime.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"AI Agent"},"/garden/ai-techniques":{"content":"Under Technique, AI Techniques covers how you apply language models and agents in software and knowledge work (patterns, frameworks, and conventions) not the ML training stack or the agent products themselves.\nIn this subcategory: portable skill formats (Agent Skills, Agent Skills - Sources), vault/brain conventions (gbrain two-layer pages, MECE resolvers), prompt and context patterns, and integration techniques (e.g. wiring agents into IDEs via Agent Client Protocol). Sibling subcategory: Artificial Intelligence & Machine Learning, classical ML/DL methods, datasets, and evaluation (see hub AI & ML).\nNot here: runnable agent tools (cursor-agent, Claude Code, Codex → AI Agent under Tool); editor platforms (Cursor → IDE); model hosts (Ollama → Platform).\nGarden stance: trial gbrain patterns (two-layer pages, light MECE resolvers), adapt to your vault, not the full opinionated stack; adopt Agent Skills where your agent supports SKILL.md; assess new skill sources before importing. Pair techniques with adopt cursor-agent / Cursor for execution, not omnichannel bots (hold under AI Agent).\nTag an item here when the note documents a repeatable pattern for using AI in systems, not a vendor SKU or raw ML theory.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"AI Techniques"},"/garden/ansible":{"content":"Ansible is agentless second touch provisioning: YAML playbooks drive modules over SSH from a control node (Python on the controller; remotes need only SSH and a working shell). We adopt it as the default when you must configure existing hosts, but treat ongoing Ansible runs as a last resort next to immutable images, Declarative IaC, and GitOps, because mutable SSH-managed state drifts no matter how good the tool is.\nBlurb Ansible is an open-source automation tool for configuration management, application deployment, and orchestration.\nSummary When to use: bootstrap or repair VMs, brownfield servers, lab machines, or one-time migrations where you cannot yet replace pets with cattle. When to avoid: day-two loop for production fleets; prefer golden AMIs/images, Terraform / cloud APIs for first touch, Kubernetes / Dev Container for runtime shape, and git-reconciled desired state.\nCompared to Salt Stack (hold) and legacy Capistrano (hold), Ansible wins on simplicity: no agent daemon, inventory + playbooks in git, dry-run via ansible-playbook --check. Controller needs SSH access (keys, ideally passwordless sudo on targets).\nDevSecOps framing: provisioners are a smell. Use Ansible to reach a reproducible baseline, then stop mutating and move config into code the platform reconciles.\nDetails Core concepts\nTerm Meaning Inventory Hosts and groups (/etc/ansible/hosts, file, or ANSIBLE_INVENTORY) Playbook YAML describing desired state for a set of hosts Task One module invocation with arguments (in a playbook) Module Unit of work; must exit 0 and emit JSON on stdout Fact Host metadata from setup/gathering; variables for later tasks Ad hoc ansible CLI one-liners; fine for debugging, not for production shape Typical flow\nInstall controller and collections (once) SSH access and sudo policy on targets (once per host) Define inventory → playbook → ansible-playbook --check → fix → apply Retire ongoing playbook churn once images or cluster config own the shape Ecosystem: Ansible Galaxy roles; Red Hat Ansible Automation Platform / AWX for UI, RBAC, and job scheduling when a team outgrows CLI-only workflows.\nReferences\nQuick start video Ansible documentation Ansible book ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Ansible"},"/garden/apache-airflow":{"content":"Apache Airflow is a Python-centric workflow orchestrator for DAGs of tasks (operators, sensors, schedules) with a central metadata database, scheduler, and workers. We rate it assess: the default incumbent for data engineering batch/ETL, but heavy to operate; prefer Argo Workflows (trial) on Kubernetes or Dagu (assess) when you want lighter ops for new DAG work.\nBlurb Apache Airflow is an open-source platform for developing, scheduling, and monitoring batch-oriented workflows.\nSummary Role: orchestrate Workflow steps (extract, transform, load, ML feature jobs, reports), not application deploy. Different lane from GitHub Actions / CI-CD Tools (PR build/test) or ArgoCD (GitOps sync).\nWhen to assess / keep:\nMature data platform already on Airflow (operators, plugins, SLAs) Python-first DAGs with rich scheduling (cron, backfill, datasets in Airflow 2.x+) Team staffed to run scheduler, metadata DB, and worker pools When to choose alternatives instead:\nNeed Prefer Steps are K8s pods, artifacts between tasks Argo Workflows Single VM, YAML DAGs, minimal infra Dagu CI on merge to main GitHub Actions K8s-native CI CRDs Tekton (assess) Ops cost: Airflow expects persistent metadata (Postgres/MySQL), a scheduler process, executors (Celery, KubernetesExecutor, etc.), and monitoring. That is justified at data-platform scale; it is overkill for small automation.\nDetails Topic Notes Model DAGs in Python (@dag, operators); UI for run history Executors Local, Celery, Kubernetes; pick based on isolation and scale Secrets Connections/variables in metadata DB; integrate Vault/cloud secret managers Testing Unit-test DAG structure; use staging env for integration Security Lock down web UI, RBAC, and who can trigger DAGs Garden pattern: assess for data/ETL estates; do not default new org-wide automation on Airflow if Argo Workflows or Dagu fits. Cross-link from Dagu and Argo Workflows when comparing orchestrators.\nReferences\nApache Airflow documentation ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Apache Airflow"},"/garden/api":{"content":"Under Technique, API covers how systems expose and consume programmatic interfaces, contracts for moving data and invoking behavior across process and network boundaries.\nIn this subcategory: style and protocol choices for service interfaces, REST (default for HTTP APIs), gRPC (RPC/protobuf where it fits), GraphQL (query layer; hold for new greenfield work in our garden). Related: OpenAPI for describing and documenting HTTP APIs; RPC as the broader pattern gRPC builds on.\nGarden stance: adopt REST with OpenAPI specs for most HTTP services. assess gRPC for internal high-performance or tooling-shaped RPC (e.g. provider plugins). hold GraphQL unless you have a concrete hyper-scale or client-heterogeneity problem it solves better than REST + OpenAPI.\nTag an item here when the note is about an API style or protocol, not a specific product SDK or agent tool surface.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"API"},"/garden/argo-workflows":{"content":"Argo Workflows is a Kubernetes-native workflow engine: DAGs and steps run as pods, with retries, artifacts, parameters, and scheduling. We rate it trial for in-cluster CI, data, and ML pipelines; reach for it when workflows must live on the cluster; prefer hosted CI or lighter orchestrators when you do not need K8s execution.\nBlurb Argo Workflows is an open source container-native workflow engine for orchestrating parallel jobs on Kubernetes.\nSummary Role: run workflows (build, test, ETL, training, batch jobs); not deploy apps. Pair with ArgoCD for GitOps delivery after images and manifests are ready.\nWhen to use: teams already on K8s needing DAG semantics, fan-in/fan-out, artifact passing between steps, or long-running parallel jobs; ML/data platforms that want everything as YAML CRDs on the cluster.\nWhen to skip: simple PR pipelines (GitHub Actions / GitLab CI are enough); no Kubernetes; small teams that should not operate a workflow control plane, consider Dagu (assess) for single-binary YAML DAGs off-cluster.\nVs alternatives: Tekton (assess) is also K8s-native CI but less mature for complex DAGs and harder to debug. Jenkins (hold) on-cluster is a security and ops liability, Jenkins X explicitly points to Argo Workflows instead. Apache Airflow is the non-K8s incumbent for data DAGs; Argo Workflows fits when pods are the unit of work.\nDetails Topic Notes Model Workflow CRDs; steps as containers; templates for reuse Artifacts S3/GCS/Artifactory-style artifact repository recommended at scale UI Argo UI for run history, logs, resubmit Events Often paired with Argo Events for event-driven triggers (separate project) Ops Controller + workflow RBAC; watch etcd load and archived workflows Ecosystem: Same Argo Proj family as ArgoCD but independent install; do not confuse deploy (CD) with orchestration (workflows).","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Argo Workflows"},"/garden/argocd":{"content":"Argo CD is a declarative GitOps controller for Kubernetes: it watches Git (or Helm chart repos), compares desired state to the live cluster, and reconciles drift. We adopt it as the default continuous-delivery plane when workloads run on K8s (especially multi-cluster) instead of pushing deploy keys through Jenkins-style imperative CD.\nBlurb Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.\nSummary Role: deploy and sync, not build. CI (image build, tests) stays in your pipeline; Argo CD applies manifests after merge. It understands plain YAML, Helm, Kustomize, Jsonnet, and plugins; each tracked as an Application (and optionally grouped via app-of-apps).\nWhen to use: any team practicing Git-as-source-of-truth for cluster config; hub-and-spoke or many clusters from one control plane; need UI for diff, sync, rollback, and health.\nWhen to skip: no Kubernetes; single-cluster experiments where a simpler pipeline suffices; orgs not ready to secure Git repo permissions and secrets outside Git (see GitOps challenges).\nPairs with: Policy as Code and admission checks on the PR path; Shift Left review before merge; Continuous Deployment as the outcome Argo CD enforces.\nNot the same as: Argo Workflows (CI/data/ML pipelines on K8s). For legacy VM deploys, Ansible may still bootstrap nodes; Argo CD owns the cluster layer.\nDetails Topic Notes Sync Manual, automatic, or selective; prune and self-heal options Health Built-in resource health; custom health via Lua Multi-cluster Register clusters; one Argo CD instance can manage many Secrets Never commit plaintext. Use sealed secrets, external secrets operators, or SSM/Vault integrations RBAC Project-scoped apps; integrate SSO (OIDC) for the UI Alternatives: Flux CD is the other common GitOps engine (similar model, different UX). Prefer one per estate; mixing controllers on the same apps causes pain.\nReferences\nDocumentation CNCF webinar , Argo at enterprise scale ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"ArgoCD"},"/garden/artificial-intelligence-machine-learning":{"content":"Under Technique, Artificial Intelligence & Machine Learning covers methods and practices for building systems that learn from data or mimic intelligent behavior; not specific agent products, model hosts, or IDEs.\nIn this subcategory: classical and deep learning patterns, training/evaluation discipline, feature and pipeline thinking, and applied ML workflow (experimentation, datasets, metrics). Use the vault hub AI & ML for deeper reading lists and definitions (AI ⊃ ML ⊃ deep learning).\nSibling subcategory: AI Techniques, how you apply models in software (e.g. Agent Skills, gbrain two-layer docs, prompt patterns). Not here: runnable agent tools (cursor-agent, Claude Code → AI Agent under Tool); model platforms (Ollama, cloud APIs → Platform); competitions/community hosts like Kaggle (platform/community, not a technique).\nGarden stance: learn fundamentals here; adopt bounded agents and editors (Cursor, cursor-agent) for day-to-day engineering. Prefer explicit pipelines (Dagu) plus targeted LLM steps over undifferentiated “AI everywhere.” See projects/research/morning-briefing-personal-automation-platform for automation boundaries.\nTag an item here when the note is about an ML/AI technique or practice, not a vendor product or agent runtime.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Artificial Intelligence & Machine Learning"},"/garden/asciiflow":{"content":"AsciiFlow is a browser-based editor for drawing simple boxes-and-arrows diagrams as plain ASCII. We rate it hold: fine for a one-off sketch you paste into chat or a comment, but not for diagrams you expect to maintain. Use Mermaid (text in git) or Draw.io (formal visuals) instead.\nBlurb Draw ASCII diagrams in your browser.\nSummary When it helps: quick architecture napkin sketches, Slack/PR comments, or README boxes where a PNG feels heavy and you already live in monospace.\nWhy hold: drawings are not structured source; resizing, diffs, and refactors are painful; output must render in a monospace font or alignment breaks; no real collaboration or export story compared to Diagramming tools you version-control.\nGraduate to: Mermaid when the diagram should live in Markdown/docs and evolve with the repo; Draw.io when you need layers, icons, or shareable .drawio files.\nDetails Topic Notes Output Copy-paste ASCII; treat as disposable unless you snapshot it Rendering Test in target font (GitHub, terminal, email) before publishing Maintenance Manual edits only, no codegen from code or IaC ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"AsciiFlow"},"/garden/auth0":{"content":"Auth0 is a managed identity platform (now part of Okta) for embedding sign-up, login, and API access in applications, especially B2B SaaS products. It speaks OIDC / OAuth 2.0 and offloads passwords, social login, MFA, and hosted Universal Login. We rate it assess: strong for fast product auth, but weigh cost, tenant-model fit, and exit strategy before you bake it in.\nBlurb Auth0 is an easy to implement, adaptable authentication and authorization platform.\nSummary Positioning: developer-first IdP below enterprise workforce suites (Okta workforce, Ping, etc.) but overlapping Okta’s own portfolio since the acquisition; clarify sales and contract path if you already standardize on Okta.\nWhen to assess: greenfield SaaS needing orgs/tenants, social and enterprise connections (SAML/OIDC), machine-to-machine APIs, and customizable login UX without building auth from scratch.\nWhen to look elsewhere: single-tenant apps with simple needs (framework auth or cloud IdP may suffice); strict data residency or air-gap (self-hosted Keycloak-class options); deep B2B2B product-led onboarding layers (compare FrontEgg and similar “user management” platforms).\nOps & security: treat Auth0 as critical infrastructure; enable MFA for admins, lock down Management API tokens, use DevSecOps review on Rules/Actions code, and pair workforce access patterns with Access on Demand where production elevation matters. Boundary (Hashicorp) and other tools can consume OIDC from Auth0 for human access to infra.\nDetails Topic Notes Protocols OIDC, OAuth 2.0, SAML for enterprise connections B2B Organizations, roles, invitation flows; map to your RBAC model in the app Pricing MAU and feature tiers bite at scale; model cost before commit Portability Standards help, but Rules/Actions and hosted UX create migration work Compliance Verify region, logging, and audit needs for your sector Garden stance: assess on every new product; prove you need a dedicated embedded IdP vs. cloud-native identity (e.g. Cognito, Entra, Google) or building on a framework.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Auth0"},"/garden/aws":{"content":"Amazon Web Services was the first hyperscale Cloud and still has the broadest catalog, but we rate it hold for new work: insecure-by-default patterns, opaque “managed” shared-responsibility gaps, and a sprawl of services that encourage operational debt. Prefer Google Cloud Platform (adopt as multi-cloud spearhead) or Azure (assess, with eyes open) when a comparable capability exists; use Hybrid Cloud to place each workload on the best cloud, not lift-and-shift clones.\nBlurb AWS is the world’s most comprehensive and broadly adopted cloud.\nSummary Why hold: IAM/console complexity, historical foot-guns (public S3, over-broad roles), and services marketed as fully managed that still leave patching, scaling, and security on your team. Total cost often surprises once egress, support, and “almost managed” add-ons stack up.\nWhen AWS anyway: existing estate, partner/marketplace requirements, a service with no peer (rare and shrinking), or regulated footprints already certified on AWS. In those cases, contain blast radius, Terraform, guardrails, DevSecOps gates, and avoid pet clusters on AWS EKS (also hold; prefer Google GKE for greenfield K8s).\nGarden pattern: do not default new products to AWS; map each capability to GCP/Azure first. If you must stay, treat AWS as legacy platform to strangle over time, not the greenfield standard.\nDetails Topic Notes Strengths Mature marketplace, global regions, hiring pool familiarity Weaknesses Default-deny is opt-in discipline; service matrix overwhelming K8s See AWS EKS; use only when tied to AWS Secrets Secrets Manager is fine when already on AWS; still design rotation and IAM boundaries Exit Data egress and proprietary APIs are the real lock-in; design portable interfaces ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"AWS"},"/garden/aws-eks":{"content":"Amazon EKS is AWS’s managed Kubernetes control plane. We rate it hold: the weakest of the major managed K8s options in our experience. Use only when the workload is already bound to AWS (VPC, IAM, RDS/LB integrations, compliance footprint). For greenfield clusters, prefer Google GKE on Google Cloud Platform.\nBlurb Amazon Elastic Kubernetes Service (Amazon EKS) is a managed Kubernetes service to run Kubernetes in the AWS cloud and on-premises.\nSummary Why hold: control-plane cost per cluster, IAM/VPC wiring complexity, CNI and add-on versioning friction, and “managed” still leaves node patching, ingress, and security baselines on you. Teams often underestimate day-2 ops compared to GKE’s tighter integration with the platform that invented K8s.\nWhen EKS anyway: mandated AWS-only estate, tight coupling to ALB/NLB, IAM roles for service accounts (IRSA) patterns already standardized, or multi-tenant AWS accounts where egress to another cloud is blocked.\nContain damage: Terraform (or equivalent) for clusters and add-ons; ArgoCD for GitOps; DevSecOps admission and image policies; avoid snowflake node groups; plan upgrades and add-on compatibility explicitly.\nDetails Topic Notes Compute Managed node groups, self-managed, or Fargate; each trades cost vs. ops burden Networking VPC CNI IP consumption; plan subnets and prefix delegation early Identity IRSA for pod AWS API access; prefer over long-lived node instance roles Alternatives Google GKE for new multi-cloud or GCP-first platforms Distro EKS Distro / EKS Anywhere for on-prem parity; still AWS-flavored ops Aligns with parent AWS (hold): do not choose EKS because “we’re on AWS”; choose it only when K8s on AWS is the explicit requirement.","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"AWS EKS"},"/garden/azure":{"content":"Microsoft Azure is Microsoft’s hyperscale Cloud, strong when you are already in the Microsoft identity and productivity stack, but we rate it hold for new platforms: recurring security incidents, opaque shared-responsibility “managed” services, and incentive-driven market share (startup credits) that do not equal fit-for-purpose engineering.\nBlurb Azure. The cloud for all.\nSummary Why hold: Microsoft’s cloud security track record is a material risk, e.g. reporting in February 2024 on large-scale Azure-related exposure tied to 2023 activity. Treat Azure as legacy or compliance-driven, not the default greenfield choice. Prefer Google Cloud Platform (adopt spearhead) per our multi-cloud pattern; AWS is also hold but sometimes unavoidable for marketplace or estate reasons.\nWhen Azure anyway: Entra ID (Azure AD), Microsoft 365, .NET/Windows-heavy estates, or contracts already sunk in Azure. Use Hybrid Cloud consciously; do not mirror every GCP/AWS service badly on Azure.\nSkepticism on being #2: might be more creative accounting and Hybrid Cloud requirements then actual market position. Heavy startup-credit programs can inflate “revenue” without proving teams would choose Azure soberly; validate TCO without credits before committing.\nDetails Topic Notes Lock-in M365 + Entra + Azure RBAC intertwine, exit is a program, not a toggle Parity Many services match AWS/GCP on paper; ops maturity and defaults differ Containment Terraform, policy guardrails, DevSecOps reviews on every subscription Greenfield Default away unless Microsoft stack is the explicit requirement ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Azure"},"/garden/bitbucket-pipelines":{"content":"Bitbucket Pipelines is Atlassian’s hosted CI service for Bitbucket Cloud repos. YAML-defined steps run in Docker images on Atlassian infrastructure. We assess it when the org is already on Bitbucket; default path for new work remains GitHub Actions unless licensing or data residency forces Bitbucket.\nBlurb Bitbucket Pipelines brings continuous integration and delivery to Bitbucket Cloud, powered by Docker.\nSummary Model: bitbucket-pipelines.yml in the repo defines pipelines, branches, and deployment steps. Runners are managed; you bring container images and secrets via Bitbucket variables.\nWhen to use: product code lives in Bitbucket Cloud; Jira/Confluence integration matters; you need Atlassian’s compliance story without self-hosting Jenkins.\nWhen to skip: greenfield repos on GitHub / GitLab; heavy custom runner fleets (compare self-hosted Actions or GitLab runners); teams standardized on GitOps tooling that assumes GitHub.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Bitbucket Pipelines"},"/garden/boundary-hashicorp":{"content":"HashiCorp Boundary is an identity-aware proxy for just-in-time access to hosts and services (SSH, RDP, databases, Kubernetes) without handing users long-lived credentials or flat network VPNs. We rate it assess: strong fit for ZTNA / Access on Demand patterns when you already run the HashiCorp stack; prove ops appetite before committing (controllers, workers, Vault integration, session recording).\nBlurb Boundary provides identity-based access to dynamic infrastructure with fine-grained authorizations and session visibility.\nSummary What it does: brokers connections after OIDC/SSO auth; issues short-lived credentials (often via Vault); logs sessions for audit. Replaces “SSH keys on a bastion” and broad Tailscale-style network access when you only need specific targets.\nWhen to assess: multi-cloud or dynamic infra (Nomad, VMs, K8s) with compliance pressure for no standing privilege; teams standardizing on HashiCorp (Terraform, Vault) and wanting one access plane.\nWhen to skip: simple static fleets with mature SSO + AoD elsewhere; orgs avoiding HashiCorp post-IBM acquisition complexity; need full L3 VPN (use Tailscale sparingly, not Boundary).\nEditions: Community (self-managed), Enterprise, and HCP Boundary (managed). Match edition to audit requirements (session recording, multi-tenancy).\nDetails Topic Notes Identity Entra, Okta, Ping, or any OIDC IdP, pairs with Auth0-class providers at the edge Secrets Dynamic credentials via Vault; plan Vault HA before Boundary production Model Targets, host catalogs, roles; map to RBAC and break-glass runbooks Technique Implements Zero Trust Network Architecture principles in practice ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Boundary (Hashicorp)"},"/garden/capistrano":{"content":"Capistrano is a Ruby remote-deployment DSL: SSH into servers, run recipes (cap deploy), symlink releases; classic 2000s Ruby on Rails ops. We rate it hold: it predates Declarative IaC and GitOps; imperative deploy scripts drift, hide blast radius, and fight container-native workflows.\nBlurb Capistrano is a framework for building automated deployment scripts.\nSummary Why hold: serial SSH orchestration with mutable servers is the opposite of reconciled desired state. Recipes become tribal knowledge; rollbacks depend on symlink tricks; secrets and host lists rot in repo or .cap config.\nHistorical credit: helped teams practice repeatable deploys before modern DevSecOps, but that problem is better solved today without Capistrano.\nUse instead\nNeed Prefer Configure existing VMs Ansible (adopt, last-resort second touch) Provision cloud shape Terraform + Declarative IaC Run the app Kubernetes + ArgoCD (GitOps CD) Legacy Rails monolith Strangle to containers/CI; don’t extend Capistrano Details Imperative IaC: same failure mode as Imperative IaC generators; hard to review, hard to test, hard to audit. Migration: freeze Capistrano recipes; mirror deploy steps in pipeline + declarative infra; retire SSH-based deploy users when apps move to K8s or immutable images. ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Capistrano"},"/garden/cattle-not-pets":{"content":"Cattle Not Pets\nOnce 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/","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Cattle Not Pets"},"/garden/cdks":{"content":"CDKs\nCloud Development Kits (AWS CDK and similar) generate infrastructure from general-purpose programming languages. They are a form of Imperative IaC: convenient for prototypes, but they obscure blast radius and often lead to regret at scale. We rate CDKs hold for the same reasons as Pulumi; prefer Declarative IaC with Terraform when your team can use it.","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"CDKs"},"/garden/cel":{"content":"CEL\nCommon Expression Language (CEL) is Google’s sandboxed, non-Turing-complete expression language designed for high-frequency evaluation in security and policy contexts. It intentionally has no side effects, no I/O, and no loops , just deterministic expression evaluation over structured data. This makes it safe to embed directly in control planes without the overhead of a full policy engine.\nCEL is gaining traction in the Kubernetes ecosystem: as of k8s 1.26, ValidatingAdmissionPolicy uses CEL natively, reducing the need for external webhook-based admission controllers. It is also used in Firebase security rules, Google IAM conditions, and Envoy RBAC. Worth assessing if you are managing Kubernetes clusters or building systems that need embeddable, user-defined policy expressions.\nBlurb CEL is a non-Turing complete language designed for simplicity, speed, safety, and portability. CEL evaluates expressions and is intended to be embedded in applications for use cases from config validation to policy enforcement.\nSummary CEL sits between a simple expression evaluator and a full policy language like Rego (Policy as Code / OPA). For Kubernetes specifically, the native ValidatingAdmissionPolicy integration in 1.26+ is a compelling reason to learn it , it removes the operational burden of running a separate admission webhook. For more complex, cross-cutting policy logic, OPA/Rego via Conftest remains more capable. Assess CEL if you are already invested in the Kubernetes control plane; it will likely become unavoidable there.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"CEL"},"/garden/chatops":{"content":"ChatOps is the practice of running operations through a chat interface: posting alerts into channels and/or invoking bots that execute commands. Platforms like Slack encourage it heavily (thousands of integrations). We rate the technique hold: fine for memes and low-stakes coordination; a bad default for production DevSecOps.\nBlurb ChatOps is a collaboration model that connects people, tools, process, and automation into a transparent workflow.\nSummary Two flavors:\nAlerting in chat: notifications from CI, monitoring, or deploy tools land in team channels. Actions from chat: slash commands and bots mutate infrastructure (deploy, restart, encrypt disks, etc.). Why hold: chat is high-noise and low-audit compared to pipelines and break-glass tools. Mixing pager-worthy events with coworker banter trains people to ignore channels. Giving chat bots production credentials fails most audits and creates irresistible distraction for engineers.\nWhat to do instead: route pages through Incident Management tooling; run automation via CI, GitOps, or approved runbooks with identity separation. Use Slack (trial) for human coordination, not as the control plane.\nDetails Alerting in chat Alerting matters in DevSecOps; the signal must be scarce and actionable. A #production channel that also gets jokes and threads buries real incidents. Prefer dedicated on-call paths with explicit severity, not “whatever showed up in Slack.”\nActions from chat Triggering automation from chat sounds convenient until someone runs the wrong command in the wrong channel. Production access for chat integrations is a liability; investment here dies on compliance reviews. Tools like Keel that lean on ChatOps are long-term dead ends for the same reason.\nGarden stance hold for new ChatOps workflows. Existing bots: restrict scopes, document owners, plan retirement. Do not build new prod dependencies on chat commands.","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"ChatOps"},"/garden/ci-cd-tools":{"content":"Under Tool, CI-CD Tools groups products that run or orchestrate build, test, and deploy pipelines. The label CI/CD is overloaded: in practice it spans three technique notes we keep separate from this subcategory:\nContinuous Integration (adopt): merge often, automated build and Integration Testing on every change. Continuous Delivery (adopt): artifacts are always releasable; promotion and gates are explicit (on-prem, regulated, or multi-stage SaaS). Continuous Deployment (assess): every green mainline commit reaches production automatically; right mainly for Software as a Service with strong observability and rollback (often paired with Feature Flags). Pipeline shapes (how tools map):\nPattern What happens after CI Typical fit CI + deployment Artifact ships to production (or equivalent) after tests SaaS, K8s GitOps (ArgoCD), managed runners (GitHub Actions) CI + delivery Artifact is built, signed, and handed off; deploy is manual or downstream On-prem, air-gapped, or customer-operated installs Tag a product here when the note is about a runner, controller, or deploy server (Actions, Argo CD, Jenkins). Tag the technique notes above when the note is about the practice, not a vendor.\nGarden stance (products in this bucket):\nadopt GitHub Actions for repos on GitHub (CI and light CD in-repo). adopt ArgoCD for Kubernetes continuous delivery via GitOps (reconcile from Git; do not conflate with Argo Workflows, which is workflow/CI on-cluster). trial Argo Workflows when you need DAG-style jobs on K8s without bolting Jenkins onto the cluster. assess Tekton only if you must standardize on Kubernetes-native pipeline CRDs and accept operational complexity. hold Jenkins (security and pet-server risk; migrate off where possible) and Capistrano (SSH push deploy; superseded by GitOps or image-based CD for new work). Cross-cutting: wire DevSecOps, Policy as Code, and secret stores (HashiCorp Vault, cloud secret managers) on the PR path, not inside a pipeline server’s internal credential UI.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-21T00:00:00-04:00","tags":null,"title":"CI-CD Tools"},"/garden/claude-code":{"content":"Claude Code is Anthropic’s agentic coding assistant (terminal CLI, VS Code extension, and desktop surfaces) that reads your repo, edits files, runs commands, and follows project rules. We rate it trial under AI Agent: a strong bounded coding agent and the best fit when you live in the Anthropic stack and Agent Skills; for day-to-day interactive work we usually reach for Cursor and the cursor-agent CLI instead (editor + agent in one place).\nBlurb Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster through natural language commands.\nSummary Claude Code runs an agent loop in your project context: gather context (files, search, MCP), act (edit, bash, git), verify (tests, diffs). It supports hooks, subagents, plan mode, and the Agent Skills directory layout used across this vault (SKILL.md files).\nUse it for interactive development, refactors, and skill-driven workflows when Claude models or Anthropic billing are the constraint. It is a repo-bounded AI Agent; not an omnichannel personal bot. Always-on messaging gateways (OpenClaw, Hermes) are hold in this subcategory. For scheduled automation, prefer deterministic scripts and Dagu with LLM steps only where reasoning is required (projects/research/morning-briefing-personal-automation-platform).\nCompare Codex for the same agent-loop-in-repo pattern on OpenAI plans.\nDetails Surfaces: terminal (claude in repo root), VS Code extension, desktop app; pick one primary surface per machine. Skills: load from .claude/skills/ or shared agent-skills/ repos; see Agent Skills - Sources for curated lists. Auth: Claude subscription or Anthropic Console API; some surfaces allow third-party model routing. Fit: Tool / AI Agent, agent-loop coding tool in the workspace (IDE surfaces are deployment options, not the taxonomy). Contrast: OpenClaw / Hermes for omnichannel bots; Cursor + cursor-agent for our default editor workflow; Codex on OpenAI plans. ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Claude Code"},"/garden/cline":{"content":"Cline is an open-source (Apache 2.0) AI coding agent with one runtime across VS Code (and VS Code–compatible editors including Cursor and Windsurf), a CLI (npm i -g cline), JetBrains (early access), and an embeddable SDK. It supports Plan-and-Act workflows, multi-file edits with checkpoints, terminal execution, MCP, .clinerules, and bring-your-own-model across major providers and local inference. We rate it assess under AI Agent: mature open-source option in the same lane as OpenCode and Kilo.ai, but not our default stack (Cursor + cursor-agent for daily work).\nBlurb The Open Coding Agent. One open source agent runtime. Use it in your editor, your terminal, or embed it in your own products.\nSummary Cline (cline/cline on GitHub, 60k+ stars) runs as an IDE extension or headless CLI in your repo: coordinated edits, bash with live output, optional auto-approve, multi-agent teams with cron schedules, and integrations (Slack, Linear, GitHub Actions). Rules ship via .clinerules; skills and MCP extend tools. No vendor lock-in — Claude, GPT, Gemini, Bedrock, Azure, Vertex, Ollama, OpenRouter, and OpenAI-compatible endpoints.\nWorth evaluating when you want a widely adopted, editor-agnostic open agent without switching IDEs, or when embedding agents via the SDK. Compare OpenCode for terminal-first multi-provider hubs; Kilo.ai for a similar VS Code + CLI story; Cursor when the editor platform and subscription are the anchor.\nDetails Install: VS Code Marketplace / Open VSX (saoudrizwan.claude-dev legacy id → Cline); npm i -g cline for CLI; https://cline.bot/install Docs: https://docs.cline.bot License: Apache 2.0 (open source). Models: BYOK or local weights; all major cloud providers plus OpenAI-compatible APIs. Surfaces: IDE extension (VS Code, Cursor, Windsurf, VS Code Insiders), CLI, SDK (embed), JetBrains EA. Features: Plan/Act modes, checkpoints/undo, MCP, multi-agent coordinator, CI/Slack/Linear hooks. Fit: Tool / AI Agent — open, multi-surface coding agent. Contrast: OpenCode / Kilo.ai (peer open-source agents); Cursor + cursor-agent (default adopt stack). ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-21T00:00:00-04:00","tags":null,"title":"Cline"},"/garden/cloud":{"content":"Under Platform, Cloud groups hyperscale public clouds and their managed primitives: someone else’s computers, exposed through APIs and consoles so you rent capacity instead of racking hardware. The provider runs regions, physical security, and base virtualization; you own identity, configuration, data classification, and most “managed” service hardening under shared responsibility.\nStack (how items map):\nLayer What you get Examples in the garden IaaS Networks, compute, block/object storage AWS, Azure, Google Cloud Platform PaaS / managed runtime Less ops on the control plane; still your app and config Google GKE, AWS EKS (K8s control planes) SaaS Vendor runs the app See Software as a Service (not duplicated here) Tag a hyperscaler or cloud-native platform service here when the note is about choosing or operating on AWS/GCP/Azure (or a direct analog). Tag Hybrid Cloud for multi-cloud strategy; tag Cloud Lift and Shift when discussing migration anti-patterns.\nGarden stance (hyperscalers in this bucket):\nadopt Google Cloud Platform as the multi-cloud spearhead for greenfield: containers, data, AI, and DevSecOps-friendly defaults; do not assume 1:1 feature parity with AWS marketing checklists. hold AWS and Azure for new platforms: default away unless estate, marketplace, or Microsoft-stack requirements force them; contain legacy with Terraform, policy guardrails, and portable interfaces. hold AWS EKS when tied to AWS; prefer Google GKE for greenfield Kubernetes on GCP. adopt Hybrid Cloud as a technique when each workload lands on the best cloud for the job, not when teams mirror every service three times. Cross-cutting expectations: Cattle Not Pets for compute; Terraform (or equivalent Declarative IaC) for everything that can be code; secrets outside vendor CI credential stores; treat egress and proprietary APIs as the real lock-in. Bridge accounts with Tailscale, SD-WAN, or zero-trust patterns, not flat VPN spaghetti.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Cloud"},"/garden/cloud-lift-and-shift":{"content":"Lift and shift (rehosting) moves workloads to Cloud with little or no redesign. We rate the pattern hold: do not start new migrations this way, and do not chase hyperscaler credits by cloning the same stack on AWS, Azure, and Google Cloud Platform. Prefer Hybrid Cloud: place each workload on the provider that fits, then bridge accounts securely.\nBlurb Lift and shift is a migration strategy that moves workloads to the cloud without redesigning the application architecture.\nSummary Two failure modes we see:\nDatacenter rehost (classic): VMs and pets move to cloud IaaS unchanged. Cost savings rarely materialize; you inherit cloud complexity without cloud-native ops (Cattle Not Pets, autoscaling, managed data planes). Credit-chasing portability theater: Teams re-create RDS-like, EKS-like, and blob storage on another cloud to burn startup credits, then pay egress and dual-run tax forever. Why hold: lift-and-shift optimizes for short-term migration metrics, not operability. Stateful pets, hand-tuned networking, and proprietary managed-service mappings do not survive provider hops.\nDo instead: greenfield on Google Cloud Platform where possible; Terraform and portable interfaces for what must move; refactor toward Containerization/Kubernetes and Software as a Service shapes; use Hybrid Cloud when multiple clouds are intentional, not accidental duplication.\nDetails Anti-pattern Why it fails Better direction 1:1 service cloning across clouds Highest cost, weakest differentiation Pick best-of-breed per workload; document why it lives there Migrating databases by dump/restore monthly Downtime, drift, data egress bills Managed replication, eventing, or stay put and strangle “Same architecture everywhere” for compliance Compliance follows controls, not SKU parity Shared policy (DevSecOps, Policy as Code), different implementations Lift VMs without autoscaling groups Pay for peak 24/7 Cattle Not Pets, instance templates, rightsizing Chasing credits without TCO model Credits expire; architecture remains Model 3-year TCO including egress, support, and engineer time When rehost is acceptable: hard exit deadline from a datacenter, ISV appliance with no cloud-native path yet, or temporary landing zone with a written strangler plan and sunset date. Label it legacy containment, not the target architecture.\nReferences\nWikipedia: lift and shift Contrast: Hybrid Cloud (adopt) in this garden ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Cloud Lift and Shift"},"/garden/cloudbees":{"content":"CloudBees is the commercial steward of Jenkins and sells managed CI/CD (CloudBees CI, SaaS or self-managed controllers) plus enterprise orchestration (CloudBees CD, formerly ElectricFlow). We rate it hold with Moved Out: if you are not already on Jenkins, do not start here; if you are, treat CloudBees as containment while you plan exit to GitHub Actions, ArgoCD, or other CI-CD Tools in the garden.\nBlurb The Enterprise DevOps Leader. CloudBees powers DevOps with enterprise-grade Jenkins and DevSecOps.\nSummary What it is: a vendor layer on top of the same Jenkins architecture (controllers, agents, plugins, Groovy pipelines) with support contracts, RBAC packaging, and compliance marketing. CloudBees employs many Jenkins maintainers; choosing CloudBees does not fix Jenkins’s pet-server and credential-store problems described on Jenkins.\nWhen it appears: legacy Java shops, regulated enterprises that standardized on Jenkins a decade ago, or acquisitions inheriting CloudBees CI estates.\nWhy hold: you inherit Jenkins operational risk (upgrade fragility, plugin supply chain, non-cattle controllers) plus commercial lock-in. Managed SaaS reduces toil but does not make Jenkins cloud-native or GitOps-aligned.\nExit paths: PR-based CI on GitHub Actions; K8s delivery via ArgoCD (build in Actions, sync in Argo); in-cluster workflows via Argo Workflows instead of Jenkins X.\nDetails Topic Notes CloudBees CI Managed or self-hosted Jenkins; same pipeline model as OSS CloudBees CD Release orchestration product; separate from Jenkins but same “hold unless sunk” rule Secrets Do not rely on Jenkins credential stores; use HashiCorp Vault or cloud secret managers Security Align with DevSecOps gates on the PR path; do not treat the controller as trusted for production deploy keys Greenfield Default to adopt tools in CI-CD Tools (Actions + Argo CD pattern), not CloudBees net-new If you must stay: single controller strategy, pinned plugins, infrastructure as code for controller config, no production deploy credentials on Jenkins, DR drills that assume rebuild-not-restore.\nReferences\nCloudBees See Jenkins for OSS-specific warnings ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"CloudBees"},"/garden/clutch":{"content":"Clutch is Lyft’s extensible platform for infrastructure operations. It exposes workflows (resizing databases, rerouting traffic, running jobs) through a UI and API with pluggable components. We hold it: Lyft archived the repo on 2026-02-13 and no longer accepts issues, pull requests, or new features. Treat it as a reference for Internal Developer Platform patterns, not as a platform to adopt or extend.\nBlurb Clutch is an extensible platform for infrastructure tooling.\nSummary Architecture: backend services plus protobuf-defined workflows; frontend for operators; auth integrations for enterprise SSO.\nWhen to use: studying how a large org wrapped risky ops in audited workflows; reading source as a pattern library before building an in-house IDP.\nWhen to skip: net-new deployments; teams expecting upstream fixes, security patches, or community support. Fork only if you can own long-term maintenance.\nUpstream: github.com/lyft/clutch (archived, read-only).\nDetails Lyft posted an archival notice on the GitHub README before marking the repository read-only. Existing code and docs remain available for reference. Contact for questions was listed as clutch@lyft.com in the notice.\nTopic Notes Status Archived 2026-02-13; no new development License Apache-2.0 (fork-friendly, but maintenance burden is on you) Garden stance hold - reference only, not adopt ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Clutch"},"/garden/codacy":{"content":"Codacy is a Software as a Service code-quality and security platform that runs on Pull Requests, aggregates many linters and SAST rules, and surfaces grades and trends. We rate it assess: strong when you want a unified quality dashboard and policy gates across repos; for “fail the build on lint violations” in GitHub Actions, Super-Linter (container in CI) is simpler and free.\nBlurb Security and Code Quality for AI-Accelerated Coding\nCodacy enforces security and quality standards across the entire CI/CD. Build secure, compliant and maintainable software, from IDE to Runtime.\nSummary Role: PR-time Code Scanner under Code Linting / Shift Left: duplicate findings from ESLint, Bandit, Trivy-style checks, etc. into one UI, with org-level quality gates and coverage metrics.\nWhen to assess: many repos and languages, need central reporting for engineering leaders, or evaluating consolidation vs running linters directly in CI.\nWhen to skip: small teams with one stack; you only need deterministic lint fail in pipeline (use Super-Linter or language-native linters); IaC/policy validation (Conftest, trial) is a separate concern.\nPairs with: DevSecOps program (treat findings as backlog, not theater); required checks on main; do not replace secret scanning, dependency review, or Policy as Code on infra.\nNot the same as: SCA-only vendors, DAST, or OPA/Conftest for Terraform/K8s manifests.\nDetails Topic Notes Integration GitHub/GitLab/Bitbucket apps; status checks on PRs Findings Severity, patterns, optional AI-assisted triage (verify noise) Coverage Test coverage tracking; useful for trends, not a substitute for good tests Config .codacy.yml / UI policies; align with team Code Linting standards Cost Per-seat SaaS; compare TCO vs OSS linters in GitHub Actions Garden pattern: default local/CI lint (adopt Code Linting + Super-Linter in Actions). Pilot Codacy when visibility across dozens of services justifies another vendor.\nReferences\nCodacy Super-Linter (alternative) ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Codacy"},"/garden/code":{"content":"Code is the garden quadrant for dependencies and notations in the repo: languages and DSLs, libraries, frameworks, and test harnesses you import or reference. It is not where we classify things you operate (Tool: IDEs, CI runners, scanners) or deploy on (Platform: clouds, orchestrators), nor how you work (Technique: patterns, practices, delivery methods).\nSubcategories (how to tag):\nSubcategory Tag when… Examples Language The artifact is a syntax, spec, or data language YAML, JSON, HCL, OpenAPI, Protobuf Library Reusable package/API you link; no app skeleton Zap, tree-sitter, OpenTelemetry Framework Opinionated app stack (routing, lifecycle, layout) Ruby on Rails Test Framework Primary job is running/writing automated tests Helm Unittest, TestContainer, Container Structure Test game engine Real-time interactive runtime for games Ebitengine Direct items (no subcategory): general-purpose languages and misc code artifacts that do not fit a subcategory yet, e.g. GoLang, Python, Ruby, Dev Container specs. Prefer a subcategory when one clearly applies.\nGarden stance at this level: favor small, composable Library dependencies over heavy Framework lock-in unless the framework pays for itself; keep Language choices boring and portable (YAML, JSON adopt); implement testing via Test Framework items tied to Unit Testing / Integration Testing techniques, not ad-hoc scripts.\nPublish subcategory pages (Language, Library, etc.) so Hugo taxonomy and the public garden can list children under Code.","garden":{"kind":"category"},"lastmodified":"2026-05-21T00:00:00-04:00","tags":null,"title":"Code"},"/garden/code-linting":{"content":"Linting is automated static checking of source for style, correctness, and common bug classes before merge. We adopt it as a Technique: pick team standards once, encode them in config, and make failures block Pull Requests so review debates opinions instead of repeating the same nits.\nBlurb In computer programming, lint or linter is any tool that flags suspicious constructs, likely programming errors, bugs, stylistic errors, and suspicious constructs.\nSummary Why adopt: stops endless formatting and “you missed a semicolon” threads; catches whole classes of defects cheaply; pairs with Shift Left and DevSecOps when security linters run on the same PR path.\nLayers (typical stack):\nLayer Purpose Garden examples Editor baseline Consistent encoding, EOL, indent EditorConfig (adopt as Tool) Language linters ESLint, golangci-lint, RuboCop, etc. Run locally and in CI CI bundle One job, many linters Super-Linter in GitHub Actions Org dashboard Trends, policy across repos Codacy (assess), optional Rules of thumb:\nConfig lives in the repo (.editorconfig, eslint.config.js, .golangci.yml); version with code. Required checks on main: a green linter run is not optional. Fix or suppress with intent; do not disable rules globally because one legacy folder whines. Linters complement Code Review; they do not replace human judgment on design. Not the same as: Code Scanner products (vendor SKUs), Policy as Code for Terraform/K8s (Conftest), or Unit Testing (behavior vs style/static rules).\nDetails Topic Notes Local make lint or pre-commit hooks so devs see failures before push CI Same command as local; no “works on my machine” drift PR Report status to the PR; see Pull Request Monorepo Path filters or per-package configs to keep runtime sane AI-generated code Lint gates matter more, not less; tighten before merge When to add Codacy: many repos and languages and leaders need a single quality scorecard; otherwise Super-Linter plus native linters is enough.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Code Linting"},"/garden/code-review":{"content":"Code review is structured peer (or self) inspection of a change before it lands on the mainline. We adopt it for every team: combine human judgment on design and risk with machine gates (Code Linting, tests, Policy as Code, security scanners) on the same Pull Request.\nBlurb Code review is a software quality assurance activity in which a person or team checks source code, mainly by viewing and reading parts of it.\nSummary What good review looks like: small, focused diffs; clear description; at least one qualified reviewer when possible; all automated checks green; unresolved comments block merge.\nWorkflow: hosts such as GitHub and GitLab implement review via Pull Request / Merge Request; Gerrit uses change-based review with similar gates. The PR is the front door to Continuous Integration: nothing merges until review and CI agree.\nMachine vs human:\nLayer Catches Garden links Automated Style, static bugs, policy, unit tests Code Linting, Conftest, Codacy (assess) Human Architecture, security nuance, operability, product fit This technique Solo developers: still open a PR against main; bots and CI play the second pair of eyes.\nPairs with: Shift Left (find issues before production); DevSecOps (security findings are review backlog, not theater).\nDetails Topic Notes Size Prefer PRs that fit one context switch; split large work Blocking Failed checks, policy denies, or open required threads = no merge Rubber stamps Review for understanding, not checkbox approval Ownership CODEOWNERS or team routing for sensitive paths AI-assisted code Higher bar for human review; never skip automation Terminology: see Pull Request (GitHub PR, GitLab MR). Same practice, different vendor names.\nReferences\nWikipedia: Code review ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Code Review"},"/garden/code-scanner":{"content":"Under Tool, Code Scanner groups products that analyze source, dependencies, or committed config and report findings (usually on Pull Requests or in CI). This is the vendor and runner layer; the practice of enforcing standards is Code Linting (adopt as a Technique).\nWhat belongs here:\nKind Examples in the garden PR / repo quality platforms Codacy (assess): aggregated linters, SAST, coverage dashboards Config / IaC policy runners Conftest (trial): Open Policy Agent tests on Terraform, K8s, etc. Language-native SAST (future items) Semgrep-class tools when we add them What does not belong here:\nCode Linting the technique (how teams enforce style and static rules). Super-Linter and bare ESLint/golangci-lint configs (run via Code Linting + GitHub Actions, not a scanner SKU). DAST against running apps (Zed Attack Proxy (Zap) is dynamic testing, not repo scanning). Unit Testing / Integration Testing (behavior verification). Garden stance:\nDefault: Code Linting in-repo + Super-Linter (or per-language linters) in GitHub Actions with required PR checks. assess Codacy when you need org-wide quality scores across many repos. trial Conftest when Policy as Code on IaC is the goal; pair with Shift Left and DevSecOps stages (build/test gates, not checkbox compliance). Tag a product here when the primary artifact is a scanner service or CLI marketed for security/quality analysis. Tag CI-CD Tools when the product is primarily a pipeline server (Jenkins, Actions).","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Code Scanner"},"/garden/codeship":{"content":"CodeShip is a hosted CI service (CloudBees) for building and testing from Git repos. We hold it for net-new work: the product line is legacy relative to GitHub Actions and CloudBees’ Jenkins-focused road map. Keep only where a pipeline still runs on CodeShip and migration is scheduled.\nBlurb CodeShip helps teams release software faster by automating build and test workflows in the cloud.\nSummary When to use: sustaining existing CodeShip projects until decommission; short-term bridge during a CI migration plan.\nWhen to skip: any new repository or service; standardize on GitHub Actions, GitLab, or self-hosted Jenkins only when required by policy.\nMigration hint: reproduce codeship.yml steps as Actions workflows or reusable workflows; rotate secrets into GitHub OIDC or org vaults during cutover.","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"CodeShip"},"/garden/codex":{"content":"Codex is OpenAI’s agentic coding agent (terminal CLI (Rust), IDE integrations, and cloud tasks) built to read repos, edit files, run commands, and use MCP tools under approval policies. We rate it trial: credible alternative to Claude Code when you are on ChatGPT Plus/Pro/Business plans and want OpenAI models; Claude Code is also trial here (we usually default to Cursor + cursor-agent for daily IDE work).\nBlurb Codex is OpenAI’s coding agent. It helps you build, debug, and ship faster across terminals, IDEs, web, and mobile.\nSummary Codex runs an agent loop scoped to your codebase: sandboxed execution, configurable approval modes, subagents for parallel work, and support for the Agent Skills SKILL.md format (skills from Agent Skills - Sources often port with minimal changes). It is not the legacy GPT-3 “Codex” completion API, that model family is historical; this note is the 2025+ product (openai/codex on GitHub).\nUse for interactive coding, reviews, and refactors inside a repo. Like Claude Code, it is a bounded AI Agent workspace tool; not an always-on messaging bot (OpenClaw, Hermes are hold for that pattern).\nDetails Install: npm i -g @openai/codex, Homebrew cask, or GitHub release binaries; requires a paid ChatGPT plan or API access per OpenAI docs. Models: GPT-5.x / Codex-tuned models with adjustable reasoning; cloud and local terminal modes per release notes. Safety: sandboxing, network policies, and human-in-the-loop approvals, see OpenAI’s “running Codex safely” guidance for enterprise patterns. Fit: Tool / AI Agent, agent-loop coding tool; compare Claude Code on Anthropic plans. Skills: cross-compatible SKILL.md ecosystem noted in Agent Skills - Sources. ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Codex"},"/garden/concourse-ci":{"content":"Concourse CI is an open source CI system built around pipelines as code, immutable container builds, and the “resources / jobs / tasks” model. We assess it for teams that want self-hosted, pipeline-as-data workflows; most new work stays on GitHub Actions or Tekton on Kubernetes.\nBlurb Concourse is an open-source continuous integration and delivery tool with a pipeline-based architecture.\nSummary Model: fly CLI sets pipelines from YAML; workers run steps in containers; resources poll git, S3, and other endpoints for new versions.\nWhen to use: air-gapped or self-hosted CI with strong isolation; teams already running Concourse at scale with dedicated platform engineers.\nWhen to skip: managed SaaS preference; need rich marketplace actions (Actions wins); small teams without ops capacity to run the Concourse cluster.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Concourse CI"},"/garden/conftest":{"content":"Conftest is a CLI that runs Open Policy Agent (Rego) policies against structured config on disk: Terraform plans, Kubernetes manifests, Helm charts, Tekton, Dockerfile, and more. We trial it under Code Scanner as the default way to implement Policy as Code in CI before deploy; Policy as Code itself is adopt as a Technique.\nBlurb Conftest helps you write tests against structured configuration data. Using Conftest you can write tests for your Kubernetes configuration, Tekton pipeline definitions, Terraform code, Serverless configs or any other config files.\nSummary Role: offline policy tests (conftest test, conftest verify) with policies in Rego, results as pass/fail per file. Fits Shift Left and DevSecOps build gates on Pull Requests via GitHub Actions (or any CI).\nWhen to use: you already commit IaC and want repeatable compliance rules; OPA ecosystem is acceptable; need one tool across Terraform + K8s + misc YAML.\nWhen to skip: only application source linting (use Code Linting / Codacy); Terraform-only teams may assess Regula instead; simple Kubernetes admission without OPA ops may use native CEL (ValidatingAdmissionPolicy) for narrower rules.\nPairs with: versioned policy/ repo or directory; conftest verify for policy unit tests; admission controllers in-cluster for runtime (Conftest is pre-deploy).\nNot the same as: Codacy (multi-linter SaaS on app repos); cluster admission webhooks (different execution point).\nDetails Topic Notes Inputs Directories of manifests, JSON/YAML, HCL, Dockerfile, etc. Policies Rego under policy/; share bundles with OPA elsewhere CI Fail build on deny; pin Conftest version in workflow Terraform Test planned JSON (terraform show -json) or static .tf per your pipeline Helm Render chart then test output, or test templates with care Learning curve Rego is the cost; invest once for cross-surface rules Garden pattern: start with a small deny-list (no latest tags, required labels, disallowed ports); expand with Policy as Code maturity. For K8s-only simple rules, compare CEL before adding OPA operational surface.\nReferences\nConftest documentation Open Policy Agent ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Conftest"},"/garden/container-structure-test":{"content":"Container Structure Test (Google Container Tools) validates built container images before push or deploy: command output, filesystem paths, file contents, and image metadata. We adopt it under Code / Test Framework for any pipeline that produces Containerization artifacts, run after docker build (or equivalent) in GitHub Actions or CI.\nBlurb Container Structure Tests are a powerful framework to validate the structure of a container image.\nSummary Role: image acceptance tests defined in YAML, executed against a tag or tarball:\n1 container-structure-test test --image <image> --config <testfile> Test types:\nType Validates Command Run a command in the image; check exit code and output File existence Path present or absent File content Expected or forbidden substrings in a file Metadata Env, exposed ports, entrypoint, user, etc. When to use: golden images, minimal distroless/runtime images, and charts that bake config into the image; catch “wrong binary missing” before Kubernetes ever sees the tag.\nWhen to skip: application integration tests that need live Postgres/Kafka (use TestContainer, assess). Helm template output (use Helm Unittest, adopt).\nPairs with: Unit Testing for app code; Docker or compatible builders; Open Container Initiative image layout expectations.\nDetails Topic Notes Config One or more YAML test files; version schema in project docs CI Fail the job on any test failure; pin the CST binary version Speed Faster than booting a cluster; slower than pure unit tests Scope Image contract only, not runtime policy or network behavior Garden pattern: add CST to the image build job immediately after build and before registry push. Keep tests focused (required binaries, config files, non-root user) so flakes stay rare.\nReferences\ncontainer-structure-test ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Container Structure Test"},"/garden/containerization":{"content":"Containerization\nContainerization packages an application and its dependencies into an immutable OCI image that runs the same way on a laptop, in CI, and in production. We adopt it for services we build and operate: it enables Cattle Not Pets, pairs with Kubernetes orchestration, and satisfies portability goals from the 12 Factor App (processes, config via env, disposability).\nBlurb The Open Container Initiative is an open governance structure for the express purpose of creating open industry standards around container formats and runtimes.\nSummary What it is: a Technique (how you ship), not a single vendor product. The Open Container Initiative defines image and runtime specs; builders (docker build, buildah, kaniko) produce images; runtimes (containerd, cri-o, Podman) execute them.\nWhy adopt: reproducible builds, smaller blast radius than VMs, standard CI artifacts, and a straight path to GitOps (image tag is the deploy unit). Software as a Service and Cloud native designs assume containers.\nTooling stance in this garden:\nConcern Direction Format / runtime adopt OCI via Open Container Initiative Local dev runtime Prefer Rancher Desktop (assess) or Podman over commercial Docker Desktop (hold) Orchestration adopt Kubernetes at scale; single-node compose only for local/dev Image tests adopt Container Structure Test after build Dev environments adopt Dev Container for shared toolchain Not the same as: VMs with configuration management only; serverless functions without a container artifact (though many FaaS run OCI under the hood).\nDetails Topic Notes Images Immutable; promote by tag/digest, not mutable servers Config Inject at runtime (env, mounts, secrets stores), not bake secrets into layers Security Non-root users, minimal base images, scan in CI Lift-and-shift Containerizing a VM monolith helps packaging; still refactor for Cattle Not Pets Garden pattern: every service repo produces an OCI image in GitHub Actions (or CI), passes Container Structure Test, then deploys via ArgoCD or your CD path.\nReferences\nOpen Container Initiative ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Containerization"},"/garden/continuous-delivery":{"content":"Continuous delivery (CD) means every change that passes Continuous Integration produces a releasable artifact; production deploy is a deliberate, low-risk promotion (button, approval, or automated stage), not an emergency rebuild. We adopt CD for all products; choose Continuous Deployment (assess) only when Software as a Service economics and observability justify shipping every green mainline commit.\nBlurb Continuous delivery is a software engineering approach in which teams produce software in short cycles, ensuring that the software can be reliably released at any time.\nSummary CI vs CD:\nStage Technique Outcome Merge to main Continuous Integration (adopt) Built, tested artifact Releasable always Continuous Delivery (adopt) Artifact in registry/repo; deploy pending Auto to production Continuous Deployment (assess) No human promote step Typical delivery pipeline (after CI):\nBroader testing (system/integration in staging) Artifact signing and provenance (supply chain) Artifact publishing (registry, chart repo, installer blob) Promotion to environments (dev → staging → prod) with gates When delivery (not deployment): on-prem or customer-operated installs, regulated industries, multi-tenant SaaS with scheduled releases, or when rollback/feature control needs a human promote.\nWhen deployment instead: internal Software as a Service with Feature Flags, strong metrics, and automated rollback; see Continuous Deployment.\nTooling: GitHub Actions for build/sign/publish; ArgoCD + GitOps for K8s promote-by-sync; avoid Capistrano-style SSH push for new work.\nDetails Topic Notes Definition of done Main is green and artifact is in the catalog customers could receive Gates Manual approval, change windows, or policy checks on promote Config Environment-specific values outside the image (12-factor) Rollback Redeploy previous artifact tag; keep N-1 ready Garden pattern: adopt CD everywhere; assess auto-deploy per product. Pair with Pull Request required checks and CI-CD Tools in the garden.\nReferences\nWikipedia: Continuous delivery ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Continuous Delivery"},"/garden/continuous-deployment":{"content":"Continuous deployment extends Continuous Delivery: every change that passes Continuous Integration and downstream tests is automatically promoted to production without a manual release button. We rate it assess, not default: adopt Continuous Delivery everywhere; use continuous deployment when Software as a Service shape, observability, and release safety nets are in place.\nBlurb Continuous deployment is a strategy in which every change that passes automated tests is automatically released to production.\nSummary Where it fits:\nTechnique Production promote Continuous Delivery (adopt) Human or scheduled gate Continuous deployment (assess) Automatic on green main Prerequisites before assess becomes adopt for a product:\nFeature Flags to ship dark and enable per user/tenant Fast automated rollback (redeploy N-1 image, ArgoCD history, or equivalent) Metrics, logs, and alerts that detect regressions within minutes Staging that exercises integration/system tests meaningfully (not only unit tests) Team comfort with trunk-based flow and small batches Typical flow (SaaS on K8s): GitHub Actions builds and pushes an image on merge; ArgoCD auto-syncs the new tag to production (or progressive sync per env). Config and secrets stay out of the image per 12 Factor App.\nWhen to skip: on-prem deliverables, regulated change windows, multi-customer installs where customers control upgrade timing, or immature test/observability culture.\nNot the same as: deploying from laptops; Capistrano SSH pushes; or \"CD\" meaning only Continuous Delivery with a manual promote.\nDetails Topic Notes Risk Production blast radius is the whole pipeline’s responsibility Quality bar CI + staging must be trusted; flaky tests block all progress Compliance May still need audit trails and approval policies even if deploy is automatic Canary/blue-green Optional layers on top; not a substitute for flags and rollback Garden pattern: adopt Continuous Delivery; trial/assess continuous deployment per product after flags + observability + rollback drills. Pair with CI-CD Tools and GitOps rather than imperative deploy scripts.\nReferences\nWikipedia: Continuous deployment ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Continuous Deployment"},"/garden/continuous-integration":{"content":"Continuous integration (CI) means every change merged to the mainline is built and verified automatically before it becomes everyone else’s baseline. We adopt CI for all products: small batches, fast feedback on Pull Requests, and a green main that feeds Continuous Delivery (and optionally Continuous Deployment).\nBlurb Continuous integration is the practice of merging all developers’ working copies to a shared mainline several times a day.\nSummary What CI owns (stop here vs CD):\nStage Technique Outcome PR + merge to main Continuous Integration (adopt) Compile/build, Unit Testing, lint, fast integration tests Releasable artifact Continuous Delivery (adopt) Signed/published artifact; promote with gates Auto to production Continuous Deployment (assess) No human promote Typical CI pipeline on every PR and on main after merge:\nCheckout and dependency restore (cached) Build (compile, bundle, image build if applicable) Code Linting and static checks (Shift Left) Unit tests and targeted integration tests (containers or test env as needed) Security/policy scans on the PR path (DevSecOps, Policy as Code where IaC changes) Code Review with required status checks before merge Practices we expect:\nTrunk-based flow: short-lived branches; merge at least daily Fix forward: broken main is priority zero; revert if fix is not minutes away Same commands locally and in CI (make test, npm test, etc.) Required checks on protected main; no bypass without explicit process Tooling: GitHub Actions (adopt) for repos on GitHub; avoid new Jenkins (hold) pet servers. See CI-CD Tools for the product bucket vs these technique notes.\nAfter CI succeeds: hand off artifacts to delivery (registry, chart repo, installer) per Continuous Delivery. Deploy controllers (ArgoCD) run downstream of the build, not instead of it.\nDetails Topic Notes Scope CI proves the change integrates; it does not replace staging/system tests in CD Speed PR feedback under ~15 minutes for core paths; split slow suites Flakes Quarantine or fix; do not mute checks Monorepo Path filters so unrelated packages do not block the world Forks pull_request vs pull_request_target carefully; least privilege for untrusted code Garden pattern: adopt CI everywhere; adopt CD next; assess continuous deployment per SaaS product. Pair CI with Pull Request discipline and Code Linting required checks.\nReferences\nWikipedia: Continuous integration ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Continuous Integration"},"/garden/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.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"CUE"},"/garden/cursor":{"content":"Cursor is a VS Code–based IDE with built-in AI: Tab completion, inline edit (Cmd+K), agent panel, and cloud agents, on a paid Cursor subscription with access to many frontier models (Composer, Claude, GPT, Gemini, Grok, and more). Built by Anysphere. We adopt it under IDE as our default editor stack: one simple ecosystem for multi-model coding when paired with cursor-agent for terminal and CI workflows.\nBlurb Built to make you extraordinarily productive, Cursor is the best way to code with AI.\nSummary Cursor forks the VS Code experience and layers codebase indexing, model picker, rules, and .cursor/skills/ on top. You can dial autonomy from Tab suggestions through targeted edits to full agent runs in the editor or via cursor-agent in the shell.\nChoose Cursor when you want a single vendor for GUI + CLI agents, bring-your-own-model flexibility within their catalog, and VS Code–compatible extensions without juggling separate CLIs per provider. It is an editor platform under IDE, the agent runtime in the terminal is cursor-agent under AI Agent.\nDetails Install: https://cursor.com/download (macOS, Windows, Linux); familiar VS Code layout and keybindings. Auth: Cursor Pro / Business / Enterprise subscription for models and agent features. Models: switch per task in the agent UI; Composer family plus major third-party models. Features: Tab model, inline edit, cloud agents, PR review (Bugbot), Slack/GitHub integrations per product docs. Skills: .cursor/rules and .cursor/skills/; portable SKILL.md repos in Agent Skills - Sources. Company: Anysphere (Anysphere, Inc.) Fit: Tool / IDE, primary editor distribution (not the headless agent binary). Contrast: Claude Code, Codex, OpenCode when you want a non-Cursor agent hub; cursor-agent for the same stack in terminal/automation. ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-21T00:00:00-04:00","tags":null,"title":"Cursor"},"/garden/cursor-agent":{"content":"cursor-agent is Cursor’s terminal agent, TUI/CLI for running the same Cursor Agent in your shell, scripts, GitHub Actions, and headless automations, with frontier models and MCP. We adopt it under AI Agent as our default bounded coding agent alongside the Cursor editor: one vendor for interactive IDE work and terminal/CI agents, with skills and rules wired through .cursor/ and this vault’s research conventions (agent: cursor-agent in frontmatter).\nBlurb Ship code with agents. Right from your terminal.\nSummary The cursor-agent binary (install via Cursor’s install script) drives an agent loop in a repo: model selection (/model), shell mode, resume via cursor-agent --resume <chatId>, and cursor-agent create-chat / ls for session management. CURSOR_AGENT=1 in the environment marks agent invocations for logging and gbrain session metadata.\nUse for day-to-day coding in the terminal, headless pipelines, and GitHub Actions when you already use Cursor billing. It is a repo-bounded AI Agent; not an omnichannel personal bot (OpenClaw, Hermes are hold). Pair with Cursor under IDE when you want GUI + CLI in one stack.\nCompare Claude Code, Codex, Gemini, and OpenCode when a different vendor or open-source hub is required.\nDetails Install: curl https://cursor.com/install -fsS | bash (see Cursor CLI docs for npm and platform packages). Auth: Cursor subscription / team plan; CLI shares model access with the Cursor product. Models: Composer, Claude, GPT, Gemini, Grok, and other frontier models exposed in the CLI picker. Automation: headless mode and GitHub Actions integrations for doc updates, reviews, and custom agents. Skills: .cursor/skills/ and rules; cross-reference Agent Skills - Sources for portable SKILL.md repos. Fit: Tool / AI Agent, Cursor’s agent runtime in the terminal (Cursor is the editor under IDE). Contrast: vendor-specific CLIs when not on Cursor; OpenCode for multi-provider open source. ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"cursor-agent"},"/garden/cursor-keep-alive":{"content":"Cursor keep-alive is an HTTP API pattern for incremental results from long-running work (for example, a streaming RAG query) when you cannot rely on an open WebSocket. The name refers to an opaque cursor ID, not the Cursor IDE or cursor-agent. We rate it assess: reach for it when load balancers or WAFs block WebSockets; prefer simpler streaming when the network allows.\nBlurb The server stores partial state keyed by an opaque cursor ID; each request advances or resumes delivery. This works through load balancers and WAFs that block WebSockets, at the cost of extra storage and polling latency.\nSummary Problem: clients want token-by-token or chunk-by-chunk output from slow server work (LLM generation, retrieval, batch transforms) without holding a long-lived connection.\nPattern:\nStart (POST): enqueue work; return cursor_id (and optional first chunk). Poll (GET /…/cursor/{id}): return the next chunk, cursor position, and done / error status. Store server-side state (Redis, DynamoDB, SQL) with TTL and idempotent reads. Compared to other streaming options:\nApproach Pros Cons WebSocket Low latency, true push Often blocked or sticky-session heavy behind WAF/LB SSE (Server-Sent Events) Simple HTTP, one-way push Still long-lived; some proxies time out Cursor keep-alive Plain request/response; survives strict proxies Polling latency; you operate cursor storage Buffer then respond Simplest server Poor UX for LLM-length waits When to assess: REST/OpenAPI query APIs behind corporate ingress, Kubernetes Ingress + WAF, or serverless with hard request timeouts (start async job + poll from the client).\nWhen to skip: you control the edge and WebSockets or SSE are allowed; or latency under ~200 ms matters and polling is too coarse.\nImplementation notes: authenticate cursor IDs; bind cursors to user/session; cap concurrent cursors; garbage-collect on TTL; return 410/404 when expired. Document the contract in OpenAPI (start + poll operations).\nDetails Topic Notes Storage Hot state in Redis; durable audit log optional Idempotency Same cursor + offset returns same chunk Backpressure Client polls with If-None-Match or since offset Errors Surface terminal error on poll; do not leave cursors orphaned Security Opaque, unguessable IDs; no cross-tenant cursor reuse Origin in our writing: described for RAG query APIs in RAG Pipeline (embedding CRUD vs streaming query split).\nGarden pattern: assess for constrained HTTP edges; adopt straightforward streaming (WebSocket/SSE) when infrastructure permits.\nReferences\nRAG Pipeline (blog) ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Cursor Keep Alive"},"/garden/dagu":{"content":"Dagu\nA lightweight workflow orchestrator that runs shell-based DAGs via a single binary and YAML definitions , worth assessing as a lower-ops alternative to Airflow or Argo for teams that don't need a full Kubernetes-native pipeline engine.\nBlurb Dagu is a workflow orchestrator that lets you define workflows as Directed Acyclic Graphs (DAGs) using YAML, execute them with a single binary, and monitor them through a built-in web UI , all without a database or heavy infrastructure.\nSummary Dagu positions itself as a simpler, self-contained alternative to heavier orchestrators like Apache Airflow or Argo Workflows. Key traits:\nSingle binary; no database required; state is stored on disk YAML-defined DAGs; each step can be a shell command, sub-DAG, or HTTP call Built-in web UI; visualize DAG structure, execution history, and logs Cron scheduling; native support without an external scheduler Low ops overhead; runs on a single VM or container without Kubernetes Best suited for teams running DevSecOps pipelines, data engineering tasks, or automation workflows at modest scale who want Airflow-style DAG semantics without the infrastructure burden.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Dagu"},"/garden/dbt-core":{"content":"dbt Core is the open-source CLI that transforms data in the warehouse: versioned SQL models, tests, docs, and a DAG executed with dbt build. We rate it trial for analytics engineering in the warehouse; pair orchestration with Apache Airflow or Argo Workflows when schedules and cross-system DAGs sit outside dbt.\nBlurb dbt is a SQL-first data transformation workflow for teams who already know SQL.\nSummary Role: transform data inside the warehouse (models, tests, docs). Not a general CI-CD Tools runner; not Apache Airflow (though Airflow often triggers dbt run).\nWhen to trial:\nWarehouse-native analytics engineering (Snowflake, BigQuery, Redshift, Postgres, etc.) Git-reviewed SQL models, dbt test, and generated lineage docs Team comfortable with Jinja-templated SQL and dbt_project.yml layout When to skip:\nNo warehouse or transforms belong in an app service, not SQL models Need only light ETL scripts; a smaller tool may suffice Org will not adopt project structure (models/, macros/, seeds/, etc.) Typical flows:\nGoal Commands Fresh deps + run dbt deps then dbt build (or dbt run + dbt test) Docs locally dbt docs generate then dbt docs serve Clean artifacts dbt clean before deps when debugging compile state Details Project layout The project is the unit of work. It must include dbt_project.yml (model paths, profiles, vars). Main artifact types:\nArtifact Purpose Models Transforms; nodes in the execution DAG Snapshots SCD-style history for mutable sources Seeds CSV loads into the warehouse Tests Data quality on models and sources Macros Reusable Jinja/SQL Sources Upstream tables loaded by other tools Exposures Downstream consumers of the project Analyses Ad hoc SQL (not materialized on run) (Semantic models, metrics, and saved queries apply when using the metrics layer; see current dbt docs for your version.)\nCLI reference (common) build — run DAG in order (models, tests, seeds, snapshots as configured) run — execute models only test — run tests (usually after run) compile — render SQL without executing deps — install package dependencies debug — connection and profile diagnostics freshness — source freshness checks snapshot / seed — run those node types Incremental models on existing tables When a table is owned elsewhere but dbt should merge new rows:\nModel name matches the table (case-sensitive per adapter) materialized='incremental' unique_key set for merge/upsert incremental_strategy (append or merge per adapter) Use is_incremental() and {{ this }} for watermark logic 1 2 3 {% if is_incremental() %} AND created_at > (SELECT COALESCE(MAX(scan_timestamp), '1970-01-01'::timestamp) FROM {{ this }}) {% endif %} See Incremental models.\nGarden pattern: trial dbt for warehouse transforms; orchestrate schedules with Apache Airflow (assess) or Argo Workflows (trial) when the DAG spans more than dbt.\nReferences\ndbt documentation dbt-core on GitHub ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"dbt-core"},"/garden/declarative-iac":{"content":"Declarative infrastructure as code describes desired end state in versioned files; a planner/reconciler (Terraform, the Kubernetes API, GitOps controllers) computes and applies the diff. We adopt declarative IaC for cloud and cluster shape; hold Imperative IaC, CDKs, and Pulumi for new work when a declarative path exists.\nBlurb Infrastructure as code is the process of managing and provisioning computer data center resources through machine-readable definition files, rather than physical hardware configuration or interactive configuration tools.\nSummary Declarative vs imperative (IaC):\nStyle Artifact Garden stance Declarative IaC Desired state (HCL, YAML, JSON) adopt Imperative IaC Programs that emit or call APIs hold Why adopt: reviewers see what should exist; engines handle ordering and drift detection. Pairs with GitOps (PR-driven apply), Policy as Code (lint before merge), and Shift Left / DevSecOps.\nCommon tools (declarative lane):\nSurface Examples Cloud accounts Terraform / OpenTofu (HCL) Kubernetes manifests, Helm charts (YAML) Cluster delivery ArgoCD reconciles Git desired state Host config (when needed) Ansible playbooks (prefer images + declarative cloud where possible) DRY without going imperative: modules, variables, for_each, Helm subcharts, and policy bundles. Repetition is a design smell, not a reason to generate IaC from TypeScript.\nWhen imperative is still OK: application code, one-off scripts, and glue. The garden hold is scoped to infrastructure generators, not all programming.\nDetails Topic Notes Reconciliation Plan/apply or controller loop; fix drift instead of documenting it Blast radius Declarative plans show resource graph changes; imperative generators hide side effects State Remote state (Terraform) or etcd (K8s); back up and lock Secrets Never in Git; use secret managers and external data sources Legacy import terraformer and similar can bootstrap; then refactor into modules Garden pattern: adopt Declarative IaC everywhere you operate cloud/K8s; see Declarative Programming for the wider paradigm and Imperative IaC for what we avoid.\nReferences\nWikipedia: Infrastructure as code ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Declarative IaC"},"/garden/declarative-programming":{"content":"Declarative programming describes what outcome you want, not the step-by-step procedure to get there. We adopt it in DevSecOps: runtimes and controllers reconcile desired state (config, policy, infrastructure, queries) instead of replaying fragile instruction scripts, especially where drift and blast radius matter.\nBlurb In computer science, declarative programming is a programming paradigm that expresses the logic of a computation without describing its control flow.\nSummary Contrast declarative with imperative code that sequences mutations (for, if, imperative loops). Declarative forms include SQL (SELECT …), configuration and policy specs, functional pipelines, and desired-state manifests. The trade-off: less control flow in the artifact, more work for the engine to plan and apply changes safely.\nIn this garden, the headline application is Declarative IaC (Terraform, Kubernetes manifests, Helm Chart templates, HCL, much YAML). Imperative IaC and generator-heavy CDKs (CDKs, Pulumi) are hold here, convenient early, painful to reason about at scale. That is a scope choice for infrastructure, not a claim that imperative languages are wrong everywhere (application code, one-off scripts, and glue still use imperative style freely).\nDetails Reconciliation: desired state + diff/apply beats “run these 47 API calls in order” for production systems. Readability: reviewers see end-state; fewer hidden side effects than procedural generators. Limits: repetition without modules/macros; complex conditionals can be harder than a small program. Use tool-native abstraction (Terraform modules, Helm subcharts) instead of abandoning declarative form. Fit: Technique item, general paradigm; see Declarative IaC for IaC-specific guidance and Imperative IaC for the anti-pattern we avoid in infra. ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Declarative Programming"},"/garden/design-pattern":{"content":"Under Technique, Design Pattern groups named, reusable solutions to recurring problems in code structure and collaboration (usually at class/module granularity). Patterns are vocabulary for review and design, not a checklist to apply everywhere.\nIn this subcategory: classic OOP patterns (GoF-style), idiomatic patterns per language (for example concurrency patterns in Go), and small-scale structural choices (repository, strategy, adapter) documented as items when they deserve a garden page.\nNot here: cloud architecture patterns (put under Platform / Declarative IaC notes), Framework conventions (Rails, Spring), Specification standards (OpenAPI, ZTNA specs), or AI Techniques (prompt/skill patterns).\nSibling subcategory: Specification for externally defined standards; both sit under Technique alongside practices like Code Review and Continuous Integration.\nGarden stance:\nAdopt patterns when they name real pain (duplication, tangled dependencies, unclear extension points) and the team will maintain the abstraction. Hold pattern-first design, deep inheritance trees, and “enterprise” layers that exist only because a book said so. Prefer simple functions + data until a pattern pays rent; in Go and similar languages, favor composition over ceremony (see Go Concurrency Patterns for idiomatic concurrency patterns). How to tag an item: create a garden item (no Type) under Technique with subcategories: [\"[Design Pattern](/garden/design-pattern/)\"] when documenting one pattern’s forces, trade-offs, and when not to use it.\nCommon families (for future items):\nFamily Examples Creational Factory, Builder, Singleton (use sparingly) Structural Adapter, Facade, Decorator Behavioral Strategy, Observer, Command, Template Method References\nWikipedia: Software design pattern ","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Design Pattern"},"/garden/dev-container":{"content":"Development Containers (devcontainer.json + image) define a reproducible dev environment in OCI: toolchain, extensions, and settings live in the repo instead of on each laptop. We adopt them for team projects so dev, Continuous Integration, and onboarding share the same container definition.\nBlurb Development containers are a great way to simplify and standardize the setup of your development environment.\nSummary What it is: an open spec (not a single product). The IDE or host attaches to a container built from your repo’s .devcontainer/devcontainer.json (and Dockerfile or prebuilt image). Born in VS Code; Cursor and other VS Code–compatible editors support the same flow.\nWhy adopt:\nProblem Dev Container answer “Works on my machine” One image definition in Git Onboarding Reopen in Container instead of a wiki install guide Dev vs CI drift Same base image or Dockerfile path in CI Toolchain pins OS packages, language versions, CLIs in the image Repo artifacts:\n.devcontainer/devcontainer.json — features, mounts, ports, post-create commands Dockerfile or image: — how the environment is built Optional dev container features (composable apt/npm layers) Local runtime: needs a container engine. Prefer Rancher Desktop (assess) or Podman; Docker Desktop is hold for licensing but still common. See Containerization for the wider OCI stance.\nNot the same as: production Kubernetes manifests (deploy shape); Ansible on pets (mutable hosts); or DevSecOps policy alone without a defined dev image.\nDetails Topic Notes Secrets Use env files or secret mounts; never bake credentials into the image Performance Mount workspace with delegated volumes on macOS; cache package dirs CI Build/test inside the same Dockerfile in GitHub Actions Agents Cursor / cursor-agent run inside the container when the editor attaches IaC link Treat the dev image like Declarative IaC for the toolchain Garden pattern: adopt dev containers for multi-developer repos; pair with IDE editors that support the spec and Containerization for how images are built and scanned.\nReferences\nDevelopment Containers Specification ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Dev Container"},"/garden/devops":{"content":"DevOps is a culture and practice that breaks the wall between development and operations: ship small changes often, automate the path to production, and measure outcomes. We adopt it as the default delivery philosophy; implement it with Agile Software Development, Shift Left, and the CI/CD techniques in this garden.\nBlurb DevOps is a set of practices that combines software development and IT operations to shorten the systems development life cycle and provide continuous delivery with high software quality.\nSummary What it means in practice:\nTheme Garden expression Flow Continuous Integration + Continuous Delivery on every merge Automation CI-CD Tools (GitHub Actions, ArgoCD); Declarative IaC Collaboration Pull Request + Code Review; ops in design, dev on-call Feedback Metrics, logs, postmortems; Cattle Not Pets Improvement Blameless retros; shrink batch size Plan → code → build → test → release → deploy → operate → monitor maps to concrete gates: lint/tests on the PR, signed artifacts, promoted deploys, observability in prod.\nShift Left is how we describe moving quality, security, and ops checks earlier (same PR, same pipeline).\nGitOps is our preferred apply model: Git is the source of truth; merge triggers reconcile (infra and cluster).\nSecurity: DevSecOps (trial) extends DevOps with measurable security work in each stage, not a separate waterfall audit.\nNot the same as: buying a “DevOps platform” SKU; a dedicated ops team that only runs tickets; or Jenkins/CloudBees (hold) as the whole strategy.\nDetails Topic Notes Culture Shared ownership of running software; no “throw over the wall” Batch size Trunk-based flow; feature flags over long-lived branches Legacy Automate incrementally; do not wait for perfect greenfield Metrics Lead time, deploy frequency, MTTR, change failure rate (DORA) On-call Dev participates in production feedback loops Garden pattern: adopt DevOps as the umbrella; adopt CI/CD/GitOps/declarative infra underneath; trial DevSecOps when security needs a named program beyond shift-left gates.\nReferences\nWikipedia: DevOps ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"DevOps"},"/garden/devsecops":{"content":"DevSecOps can be thought of as DevOps with a security focus at every step. However, a better way to think about it is to treat security as a blameless iterative process.\nAccept that security is an arms race against bad actors that have more time and more resources than you. Instead of throwing in the towel, or making it someone else’s problem, DevSecOps is about realizing it is a risk. A risk that can be measured and acted upon in the existing Agile or DevOps process you are already following.\nIf you already practice DevOps and need a process to handle security concerns then DevSecOps is something to try.\nMap security work to the same delivery stages you already use: plan (threat modeling, requirements), build (SAST, dependency scan), test (DAST, fuzzing), release (signing, policy gates), deploy (hardening, secrets), operate (monitoring, IR), and improve (postmortems, control updates).\nManifesto Taking a page out of the Agile Manifesto, DevSecOps has the following manifesto:\nLeaning in over Always Saying “No” Data & Security Science over Fear, Uncertainty and Doubt Open Contribution & Collaboration over Security-Only Requirements Consumable Security Services with APIs over Mandated Security Controls & Paperwork Business Driven Security Scores over Rubber Stamp Security Red & Blue Team Exploit Testing over Relying on Scans & Theoretical Vulnerabilities 24x7 Proactive Security Monitoring over Reacting after being Informed of an Incident Shared Threat Intelligence over Keeping Info to Ourselves Compliance Operations over Clipboards & Checklists ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"DevSecOps"},"/garden/diagramming":{"content":"Under Tool, Diagramming groups products and formats for visual models of software: architecture, flows, sequences, ER diagrams, and deployment sketches. Prefer diagrams that live in Git (text or structured XML) over screenshots that rot.\nIn this subcategory:\nTool Rating Use when Mermaid adopt Markdown/docs, PRs, Hugo; sequence, flowchart, class, C4-style in repo Draw.io trial Rich layouts, icons, .drawio in repo, VS Code/Cursor extension Graphviz hold Legacy DOT pipelines only; prefer Mermaid for new text diagrams AsciiFlow hold Disposable chat/comment sketches only Not here: whiteboard photos; slide-deck-only graphics; OpenAPI specs (source of truth for APIs, diagrams are derived); screenshot tools.\nGarden stance:\nAdopt Mermaid in README, ADRs, gbrain research pages, and blog posts so diagrams diff with code. Trial Draw.io (diagrams.net) when stakeholders need polish or manual layout; store .drawio + export PNG/SVG in the repo if published. Hold one-off ASCII art (AsciiFlow) and new Graphviz unless you already depend on DOT. Practices:\nDiagram to explain, not to decorate; one diagram per question. Link diagrams from the doc that owns the decision (ADR, design note). For C4 or architecture overviews, start at context/container level; drill down in separate files. How to tag: Tool items with subcategories: [\"[Diagramming](/garden/diagramming/)\"] when the product’s main job is drawing or rendering diagrams.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Diagramming"},"/garden/direnv":{"content":"direnv loads and unloads per-directory environment variables when you cd into a project (via a hook in bash/zsh/fish). We rate it assess: strong for host-shell and multi-repo local work; less critical when Dev Container or a VM already defines the toolchain.\nBlurb direnv is an extension for your shell. It augments existing shells with a new feature that can load and unload environment variables depending on the current directory.\nSummary What it does: reads .envrc in the project root (bash-like DSL), exports vars for that directory tree, reverts when you leave. Common patterns:\ndotenv / dotenv_if_exists — load .env (keep .env gitignored, commit .envrc) layout python, layout go — wire language version managers use nix, use flake — enter Nix dev shells on cd When to assess / adopt locally:\nMany repos on the laptop with different PATH, API keys, or tool versions cursor-agent / CLI agents that expect .env loaded automatically (see vault agent-skills README) Polyglot monorepos where each subdirectory needs its own env When to skip:\nPrimary dev is Dev Container or remote environments (env is in the image) Team policy forbids shell hooks on corporate laptops You only need one global profile (use shell profile or a single version manager instead) Security: run direnv allow only after reviewing .envrc; treat untrusted clones like arbitrary shell scripts.\nDetails Topic Notes Install Package + direnv hook zsh (or bash) in ~/.zshrc CI Do not rely on direnv in GitHub Actions; set env in workflow explicitly Secrets .envrc can reference .env; never commit secrets Pairing Fits Environment Managers bucket alongside version managers, not a replacement Garden pattern: assess for host-native development; adopt Dev Container first for team parity, then add direnv where shell workflows remain.\nReferences\ndirenv documentation ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"direnv"},"/garden/dive":{"content":"Dive is a terminal UI for exploring OCI/Docker image layers: file tree per layer, layer size, and an efficiency score to spot bloat. We rate it trial for interactive image slimming during development; pair automated gates with Container Structure Test and registry scanners in CI, not Dive alone.\nBlurb A tool for exploring a docker image, layer contents, and discovering ways to shrink the size of your Docker/OCI image.\nSummary Role: developer feedback loop after docker build (or Podman): see which layers added weight, which files duplicate across layers, and what to move to multi-stage builds or .dockerignore.\nWhen to use:\nDebugging fat images before production Teaching Containerization (layer caching, multi-stage Dockerfiles) Quick review of third-party base images When to skip:\nHeadless CI-only pipelines (use dive ci only if you accept its efficiency threshold; many teams prefer CST + vulnerability scan) Teams on Dev Container only who never inspect host-built images Workflow:\n1 2 dive myimage:tag # interactive TUI dive ci --ci-config .dive-ci myimage:tag # optional CI gate Works with the docker CLI; often used with Docker / Rancher Desktop local engines. Image must be available locally (docker load if from CI artifact).\nDetails Topic Notes Efficiency score Heuristic; use as a hint, not a policy Multi-stage Dive shines when comparing builder vs runtime stages Security Layer exploration is not vulnerability scanning Complements Container Structure Test asserts image contract; Dive explores why the image is large Garden pattern: trial on repos you build; adopt CST in CI for every image you ship.\nReferences\ndive on GitHub ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Dive"},"/garden/docker":{"content":"Docker popularized containers and still owns the de facto docker CLI and Dockerfile workflow. We rate the Docker Inc product stack (hold), especially Docker Desktop licensing and hub-centric defaults: prefer Open Container Initiative-compatible engines (Rancher Desktop, Podman) for new local dev. Containerization as a technique remains adopt.\nBlurb Docker helps developers bring their ideas to life by conquering the complexity of app development.\nSummary What to separate:\nPiece Garden stance OCI images / containers adopt via Containerization docker build / BuildKit in CI Common; also available via buildah/kaniko docker CLI on laptop OK as a front-end when the engine is Rancher/Podman/containerd Docker Desktop (paid) hold for new org-wide adoption Docker Hub as only registry Avoid single-vendor lock-in; use ECR/GCR/ACR + mirrors Why hold (vendor stack): commercialization of Desktop, license audits at scale, and historical tie-in to Docker-specific paths (#category/containers era tooling). The engine (containerd, runc) is industry standard; the Docker Inc desktop bundle is not.\nWhen the CLI still makes sense: tutorials, Dev Container docs, Dive, and CI snippets that call docker. Prefer rootless/daemonless options where security policy requires it.\nAlternatives for local dev: Rancher Desktop (assess), Podman (trial in garden), or Linux VM with containerd only.\nDetails Topic Notes Dockerfile Ubiquitous; keep multi-stage, non-root, minimal bases Compose Fine for local stacks; not a prod orchestrator (Kubernetes) CI docker build or buildx; pin BuildKit; scan images in pipeline Security Do not mount /var/run/docker.sock into untrusted CI without isolation Garden pattern: adopt containers; hold betting the estate on Docker Desktop; use portable OCI tooling and document engine choice in README.\nReferences\nDocker documentation ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Docker"},"/garden/docker-compose":{"content":"Docker Compose defines multi-container apps in compose.yaml and runs them with docker compose up. We rate it hold with Moved Out of the default path: fine for local dependency stacks, not for production orchestration. For Kubernetes dev loops use Skaffold (trial) instead of growing Compose into a pseudo-prod platform.\nBlurb Compose is a tool for defining and running multi-container Docker applications.\nSummary What it is: YAML (or JSON) describing services, networks, volumes, and env for a small stack on one Docker engine. The v2 plugin (docker compose) replaced the legacy docker-compose Python binary.\nWhen Compose is still OK:\nUse Notes Local integration Postgres + Redis + app on a laptop Demos / spikes Fast bring-up without a cluster CI smoke Short-lived compose up in a job (tear down after) When to avoid (why hold):\nProduction multi-host orchestration (Kubernetes, ArgoCD, GitOps) “Compose in prod” on Docker Swarm (also hold in this garden) Teams that should invest in cluster-native dev (Skaffold, Dev Container + K8s) K8s-shaped alternative: Skaffold build/deploy/watch against a real cluster or kind/minikube, so dev matches prod networking and manifests.\nDetails Topic Notes Files compose.yaml at repo root or deploy/compose/; pin image digests for anything non-throwaway Secrets Use env files gitignored; not for prod secret stores Parity Do not assume Compose service DNS equals K8s Service names Engine Runs on Docker / Rancher Desktop / Podman compose support Garden pattern: hold as a strategic choice (moved out for prod); allow Compose only where Docker note says: local stacks, not the deployment model.\nReferences\nDocker Compose documentation ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Docker Compose"},"/garden/docker-desktop":{"content":"Docker Desktop is Docker Inc’s licensed desktop bundle: GUI, docker/docker compose CLI, optional local Kubernetes, and extensions on macOS/Windows/Linux. We rate it hold with Moved Out from adopt: do not standardize new estates on it; prefer Rancher Desktop (assess) or Podman for OCI-compatible local engines. Containerization stays adopt.\nBlurb Docker Desktop is a one-stop shop for everything you need to build, share, and run containers.\nSummary What you get: integrated container runtime, image builds, Docker Compose for local stacks, and a click-to-enable K8s cluster for laptop demos. Fits tutorials and Dev Container docs that assume a docker socket.\nWhy hold (new adoption):\nConcern Detail Licensing Commercial terms for larger companies; audits and true-ups are real Vendor stack Tied to Docker Hub defaults and Docker Inc roadmap Alternatives Rancher Desktop (open-source GUI, containerd/nerdctl, optional k8s) Policy Same garden line as Docker: portable OCI, not Desktop-as-standard When existing use is fine: teams already licensed and productive; migrate on renewal or new machine policy, not as a fire drill.\nNot a prod platform: local dev only; production stays Kubernetes + GitOps, not Desktop’s embedded cluster.\nDetails Topic Notes Dev Containers Often documented with Desktop; other engines work if the socket is compatible WSL2 / macOS VM Desktop wraps a Linux VM; know where disk and CPU go Extensions Optional; treat as supply-chain risk in regulated environments CI Do not require Desktop in pipelines; use rootless builders in GitHub Actions Garden pattern: hold for net-new org mandates; assess Rancher Desktop on the next laptop refresh. See Docker for CLI vs engine vs Desktop separation.\nReferences\nDocker Desktop Subscription overview ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Docker Desktop"},"/garden/docker-swarm":{"content":"Docker Swarm is Docker Engine’s built-in cluster orchestrator (swarm mode): managers, workers, overlay networks, and rolling service updates. We rate it hold with Moved Out: do not start new multi-host estates on Swarm. Use Kubernetes (adopt) for orchestration. Existing Swarm clusters backed by Mirantis commercial support may run until a planned migration.\nBlurb Swarm mode lets you create and manage a cluster of Docker nodes, and deploy application services to the cluster.\nSummary History: Swarm was pitched as a simpler Kubernetes alternative, then repositioned as a path from Docker Compose to multiple machines. The ecosystem standardized on Kubernetes; Docker Inc focuses on Desktop, Hub, and BuildKit, not growing Swarm as a greenfield choice.\nWho still maintains it: Mirantis (Docker Enterprise lineage) offers long-term Swarm support and security updates for paying customers (committed through 2030). That is not the same as Docker Inc driving new features in swarm mode for everyone.\nWhy hold (new adoption):\nConcern Detail Talent & tooling Helm, GitOps, operators, and cloud control planes assume Kubernetes Compose bridge docker stack deploy from Compose is a dead-end for most teams (Docker Compose is local-only in this garden) Stateful workloads Persistent volumes, failover, and upgrades lag K8s patterns; gaps are unlikely to close for casual users Strategic fit Containerization stays adopt; orchestration should be K8s, not Swarm When existing use is OK: regulated estates with Mirantis contracts, small stable fleets, or brownfield until migration is funded. Plan exit to Kubernetes + ArgoCD (or your GitOps tool), not indefinite Swarm expansion.\nDetails Topic Notes vs Kubernetes Swarm is simpler API surface; K8s wins on ecosystem, CRDs, and hiring Engine Swarm mode ships in the open-source engine docs; production support is vendor-specific Migration Map Services to Deployments; replace overlay secrets with cluster secret stores Local dev Do not use Swarm on laptops; use Compose locally and K8s (kind/minikube) for cluster parity Garden pattern: hold + Moved Out aligns with Docker Compose (no Compose-in-prod-on-Swarm) and Docker (hold vendor stack for new mandates). Net-new orchestration is Kubernetes.\nReferences\nDocker Swarm mode overview Mirantis Swarm support ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Docker Swarm"},"/garden/dotfiles-in-version-control":{"content":"Version-controlled dotfiles are shell, editor, and tool configs kept in a git repo so a machine can be rebuilt from a known baseline. We assess the practice: high value for solo reproducibility, but team standardization and secret hygiene need clear rules before adopt.\nBlurb Dotfiles are plain-text configuration files for Unix tools, typically stored in a home directory and named with a leading dot.\nSummary What it covers: .zshrc, .vimrc, Starship, tmux, Homebrew Brewfile, and similar personal setup committed beside a README and install script (or Declarative IaC-style bootstrap).\nWhen to use: you rebuild laptops often, want the same shell aliases everywhere, or document “how your machine is configured” for yourself.\nWhen to skip: orgs that mandate MDM-only laptops with no local customization; shared secrets in dotfiles (use a secrets manager instead); duplicating what Nix/Home Manager or a golden AMI already provides.\nPairs with: private repo or public repo with a secrets scrub checklist; symlink or copy install script; optional chezmoi/yadm if dotfiles span multiple machines.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Dotfiles in Version Control"},"/garden/draw-io":{"content":"Draw.io (diagrams.net) is a browser and desktop diagram editor for architecture sketches, flowcharts, network layouts, and C4-style views. We rate it trial under Diagramming: use when Mermaid (adopt) is not enough for polish, icons, or manual layout; commit .drawio source and exported SVG/PNG in the repo when the diagram must last.\nBlurb diagrams.net is a free online diagram software for making flowcharts, process diagrams, org charts, UML, ER and network diagrams.\nSummary What you get: drag-and-drop canvas, shape libraries (AWS/Azure/GCP icons, UML, BPMN), layers, and exports (PNG, SVG, PDF). Files are XML (.drawio or .drawio.xml) you can store next to docs.\nWhen trial fits:\nUse Notes Stakeholder decks Polished boxes-and-arrows for reviews Complex layout Fine-grained positioning Mermaid fights Icon-heavy architecture Cloud vendor stencils out of the box VS Code / Cursor Draw.io Integration edits files in-repo When to prefer Mermaid instead:\nDiagram lives in Markdown, ADRs, or Hugo and should diff cleanly in PRs Sequence/flowchart/class diagrams with low layout friction CI or docs site renders diagrams from text (no binary/XML churn) Why not adopt (yet): XML merges are noisier than text; easy to fork “pretty” diagrams that drift from code; temptation to screenshot instead of exporting source.\nDetails Topic Notes Storage Commit .drawio + optional diagram.png for readers who do not open the editor Desktop Desktop app for offline or large files Integrations Google Drive, OneDrive, GitHub; pick one backing store per team to avoid link rot Security Self-host or use desktop app if diagrams are sensitive; default web app is third-party hosted Graduate from AsciiFlow (hold) for anything you will maintain Garden pattern: trial alongside Mermaid adopt: default to Mermaid in git-backed docs; open Draw.io when layout or icons justify the tradeoff. See Diagramming for the full matrix.\nReferences\ndiagrams.net Draw.io VS Code extension ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Draw.io"},"/garden/dry":{"content":"Don’t Repeat Yourself (DRY) says every piece of knowledge should have a single, authoritative representation in a system. We rate hold with Moved Out: treat DRY as a smell to investigate, not a mandate to abstract. Duplication is often cheaper than the wrong shared abstraction, especially in IaC where blast radius matters more than line count.\nBlurb Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.\nSummary What DRY gets right: copy-pasted logic drifts; two sources of truth for the same rule will disagree; fixes get applied in one place and missed in another.\nWhere DRY goes wrong:\nPitfall Effect Premature abstraction One helper for two cases that later diverge False equivalence “Looks the same” code with different change reasons Mega-modules Terraform/Helm modules that couple unrelated resources Imperative escape hatch “Not DRY enough” pushes teams to Imperative IaC (hold) Better heuristics (not garden items yet):\nRule of Three: duplicate until the third similar case proves the abstraction. AHA: avoid hasty abstractions; let structure emerge from real variation. WET in IaC: a little repetition keeps plans readable and limits coupling. Garden alignment: Declarative IaC (adopt) stays DRY enough via modules, variables, for_each, Helm subcharts, and policy bundles without generating infrastructure from application languages. Repetition is a design smell, not a reason to adopt CDK/Pulumi.\nDetails Context Guidance Application code Extract when behavior and change rate align; keep tests on both call sites during migration Terraform / OpenTofu Prefer small modules with clear inputs/outputs over one module that owns an entire account Kubernetes / Helm Subcharts for real reuse; copy YAML when teams and lifecycles differ Policies One Rego/Sentinel bundle per concern; do not merge unrelated checks for line count Reviews Ask “what changes together?” not “how many times does this string appear?” IaC-specific: minimizing blast radius beats minimizing bytes. Explicit duplication in two stacks is often safer than a shared module that forces unrelated environments to upgrade together.\nWhen repetition is OK: similar HCL for dev and prod with intentional differences; two resources that look alike but have different owners or compliance tags; bootstrap code you expect to delete.\nGarden pattern: hold DRY as a default team slogan; Moved Out from treating it as a top-level principle. Prefer Declarative IaC composition tools over imperative generators justified only by DRY.\nReferences\nWikipedia: Don’t repeat yourself Please do repeat yourself: DRY is dead The fallacy of DRY ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"DRY"},"/garden/dumb-init-for-containers":{"content":"dumb-init is a minimal PID 1 wrapper for Linux containers. It forwards signals to the child process and reaps zombie processes. We adopt it when the main process is not designed to run as init (most app images).\nBlurb dumb-init runs as PID 1, acting like a simple init system. It launches a single process and then proxies all received signals to a session rooted at that child process.\nSummary Problem: Docker and Kubernetes start your app as PID 1. Without a proper init, SIGTERM handling and zombie reaping break. Shell wrappers as PID 1 add their own signal quirks.\nWhen to use: production images where the entrypoint is a single binary or script; you need reliable graceful shutdown on docker stop / pod termination.\nWhen to skip: distroless images that already ship a correct init; images using tini or Kubernetes shareProcessNamespace patterns you have standardized on.\nUsage sketch: install the static binary in the image and set ENTRYPOINT [\"/usr/bin/dumb-init\", \"--\"] with CMD for the app.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Dumb-init for containers"},"/garden/earlybird":{"content":"EarlyBird (Amex, Go) scans repositories for secrets, PII, weak crypto, and key material in source, comments, and committed files. We rate it assess: promising Code Scanner for Shift Left / DevSecOps, but tune false-positives.json and ignore rules before gating merges. Complements Policy as Code (IaC shape), not a replacement.\nBlurb EarlyBird is a sensitive data detection tool capable of scanning source code repositories for clear text password violations, PII, outdated cryptography methods, key files and more.\nSummary What it does: CLI go-earlybird against local paths, remote Git URLs, pre-commit hooks, or a REST API. Output to console, JSON, or CSV. Maps findings to CWE classes (hardcoded credentials, cleartext storage, weak PRNG, suspicious comments, etc.).\nWhen to assess:\nFit Notes Org-wide secret hygiene Batch scan many repos; export JSON for SIEM or PR comments Pre-commit / CI Block commits with high-confidence hits before push Beyond regex grep Rule packs for PII patterns, crypto smells, inclusivity scans Why still assess (not trial/adopt):\nTuning cost: expect a false-positive budget (see pilot below). Overlap with gitleaks, TruffleHog, detect-secrets; pick one standard per org. Does not cover dependency CVEs (Codacy-class SAST) or IaC policy (Conftest). Garden stance: run a pilot on representative repos; commit false-positives.json and .ge_ignore to Git; wire into GitHub Actions only after signal-to-noise is acceptable.\nDetails Topic Notes Install build.sh + install.sh (Linux/macOS) or Windows build.bat; config under ~/.go-earlybird Scan go-earlybird --path=... or --git=https://... Ignore files .ge_ignore with .gitignore globs (IGNORE.md) Line ignore Comment containing EARLYBIRD-IGNORE on that line False positives false-positives.json: Codes, Pattern (regex), FileExtensions, Description (docs) Example false-positive rule (ignore rules 1, 2, 4 when pattern abc matches):\n1 2 3 4 5 6 7 8 { \"rules\": [{ \"Codes\": [1, 2, 4], \"Pattern\": \"abc\", \"FileExtensions\": [], \"Description\": \"Just because\" }] } Rules apply when both Pattern and FileExtensions match (empty FileExtensions = all extensions). To disable a code entirely, use pattern .*. To skip markdown: \"FileExtensions\": [\".md\"].\nPilot snapshot (internal, 2024): 21 false positives, 19 valid, 3 noise; re-run after rule tuning before org mandate.\nPipeline placement: same PR path as lint (Continuous Integration step 5 in garden CI model); do not rely on scanner alone without secret rotation process.\nReferences\namericanexpress/earlybird Usage Hooks (pre-commit) ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Earlybird"},"/garden/ebitengine":{"content":"Ebitengine (pronounced “eh-bee-ten-gin”; formerly Ebiten) is an open-source 2D game engine for GoLang. It targets desktop and mobile from a single Go codebase with a small API surface. We rate it assess: strong fit if you already standardize on Go and need a lightweight 2D stack, but it is a niche choice compared to Unity, Godot, or web-first engines.\nBlurb Ebitengine is an open source game engine for the Go programming language. Ebitengine’s simple API allows you to quickly and easily develop 2D games that can be deployed across multiple platforms.\nSummary Ebitengine wraps rendering, input, and audio for 2D games without pulling in a heavy editor or scripting layer. You write ordinary Go, which makes it easy to share logic with servers and tools your team already ships in GoLang. The trade-off is ecosystem size: fewer tutorials, assets, and hiring signals than mainstream engines.\nDetails Platforms: Windows, macOS, Linux, browsers (via WebAssembly), iOS, Android, Nintendo Switch (with license). Model: immediate-mode 2D drawing; no built-in scene editor, games are code-first. Strengths: simple API, pure Go workflow, cross-compile story, active maintainer (Hajime Hoshi). Limits: 2D only; not the right default for 3D, AAA tooling, or teams without Go experience. When to trial: prototypes, tools, or indie 2D titles where Go is already the org language. ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Ebitengine"},"/garden/editorconfig":{"content":"EditorConfig is a repo-root .editorconfig file plus editor plugins that apply shared whitespace, encoding, and newline rules before language formatters or linters run. We adopt it as the bottom layer of Code Linting: cheap consistency across VS Code, Cursor, JetBrains, and vim without debating tabs in every PR.\nBlurb EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs.\nSummary What it fixes: UTF-8 vs Latin-1 surprises, CRLF vs LF churn, trailing whitespace noise, and “my editor used 2 spaces, yours used 4” diffs. It does not replace ESLint, Prettier, golangci-lint, or Super-Linter; it sets the baseline those tools assume.\nWhy adopt:\nBenefit Detail VCS-friendly Small text file; merges are rare Editor-agnostic Plugins for most IDEs; native support in some Shift left Applies on save locally, same rules in Dev Container if the plugin is installed Pairs with CI Linters still enforce logic; EditorConfig reduces formatting-only failures Rules of thumb:\nCommit .editorconfig at repo root with root = true. Use globs per language ([*.{js,ts}], [*.go], [*.md]). Do not duplicate every Prettier option; let formatters own language semantics. Document team choices in Code Linting standards, not in wiki-only tables. Details Example baseline (team defaults; adjust per repo):\n1 2 3 4 5 6 7 8 9 10 11 12 13 root = true [*] indent_style = space end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = false [*.md] indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true Property Typical use indent_style / indent_size Spaces vs tabs per language end_of_line lf for cross-platform repos charset utf-8 default trim_trailing_whitespace Cleaner diffs (often off for .md if needed) insert_final_newline POSIX text files; may differ for * vs *.md Monorepos: nested .editorconfig in packages is allowed; closest match wins. Prefer one root file unless a subtree truly needs different EOL rules.\nCI: optional editorconfig-checker in GitHub Actions if editors without plugins are common; most teams rely on linters plus local plugins.\nNot the same as: .gitattributes (Git checkout normalization); Prettier config; Policy as Code.\nGarden pattern: adopt .editorconfig on every new service repo before adding language linters. Listed under Code Linting editor baseline row.\nReferences\nEditorConfig Formal specification ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"EditorConfig"},"/garden/enterprise-ready":{"content":"EnterpriseReady is a feature guide and checklist (from Replicated’s study of leading B2B SaaS) for what procurement and security teams expect before they buy. We rate it trial with Moved In: use it as a product roadmap lens when moving upmarket, not as a substitute for 12 Factor App engineering discipline or DevSecOps execution.\nBlurb Created to help people build software for the enterprise, based on a study of the 50 leading SaaS applications.\nSummary Analogy: 12 Factor App for how you sell and operate B2B software; Enterprise Ready for what enterprises ask for in the contract and admin console.\nTwelve feature areas (each has guides and tear-downs on the site):\nArea Garden links / notes Product assortment Plans, entitlements, usage limits Single Sign-on adopt OIDC/SAML for B2B Audit logs Admin-visible activity trail RBAC adopt role separation in product and ops Change management Admin comms for rollouts Product security DevSecOps, pen tests, secure SDLC Deployment options SaaS vs VPC / self-hosted tradeoffs Team management Orgs, invites, central directory Integrations APIs, webhooks, data export Reporting and analytics Value proof for buyers SLAs adopt measured commitments + SLOs GDPR Privacy program when EU customers matter When trial fits:\nFirst enterprise pipeline or security questionnaire Prioritizing backlog before SOC2-heavy sales cycles Aligning product, legal, and engineering on the same vocabulary When it is not enough: checklists do not replace pen tests, contracts, or running Policy as Code on your own infra. IdP implementation still means Auth0 / FrontEgg (assess) or cloud-native identity choices.\nDetails Topic Notes Prioritization Start SSO + RBAC + audit logs; add SLAs before promising five nines Questionnaires Map each ER section to evidence (architecture diagram, policies, screenshots) Product vs platform Some items are in-app features; others are how you run Software as a Service Podcast / tear-downs Use HubSpot/Slack/Google examples as design references, not copy-paste specs Garden pattern: trial the framework when Software as a Service goes enterprise; adopt the underlying techniques (Single Sign-on, SLAs, RBAC) as separate garden items mature.\nReferences\nEnterpriseReady Feature guides index ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Enterprise Ready"},"/garden/environment-managers":{"content":"Under Tool, Environment Managers groups utilities that shape what runs on your machine when you develop: language/runtime versions, PATH, and per-project env vars. Goal is reproducible local dev without “works on my laptop” drift. Prefer Dev Container (adopt) for team-wide parity; use this bucket for host-native workflows and polyglot laptops.\nIn this subcategory:\nTool / pattern Rating Use when Dev Container adopt (related) Team standard dev image; env in OCI, not shell hooks direnv assess Auto-load .envrc / .env per directory on cd mise (asdf successor) assess One CLI for Node, Python, Terraform pins via .mise.toml asdf assess Plugin ecosystem; legacy installs still common nvm / fnm trial Node-only version switching pyenv / rbenv assess Single-language version managers when mise is overkill Nix / flakes assess Reproducible shells; heavier ops learning curve conda assess Data science stacks with binary deps What belongs here:\nVersion pins committed in repo (.tool-versions, .nvmrc, .python-version, mise.toml) Shell hooks that load project env (direnv) Local toolchain orchestration on the host What does not belong here:\nDev Container internals (document on that item; link when env is containerized) GitHub Actions / CI images (set env in workflow YAML, not direnv) Cloud deployment environments (Kubernetes, Terraform workspaces) Application config at runtime (12 Factor App env vars in prod) Garden stance:\nAdopt Dev Container (or equivalent) when more than one engineer ships the same service. Assess direnv for multi-repo laptops and cursor-agent workflows that expect .env on cd. Assess mise (or asdf) when repos pin different Node/Python/TF versions and you want one entrypoint. Commit pin files; never commit secrets (.env gitignored, .envrc reviewed). How to tag: Tool items with subcategories: [\"[Environment Managers](/garden/environment-managers/)\"] when the product’s main job is local dev environment or version management.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Environment Managers"},"/garden/feature-flags":{"content":"Feature flags (feature toggles) decouple deploying code from exposing behavior: ship to production dark, then enable per user, tenant, or percentage. We promote to trial for Software as a Service products pursuing Continuous Deployment; stay assess for simple or on-prem services where homegrown toggles may not pay back.\nBlurb Feature Toggles (often also referred to as Feature Flags) are a powerful technique, allowing teams to modify system behavior without changing code.\nSummary Flag types (Fowler):\nType Purpose Release Hide incomplete work on main; enable when ready Experiment A/B tests and gradual exposure Ops Kill switch under load without redeploy Permission Entitlements / plan tiers (often overlaps RBAC) Why trial (SaaS + CD):\nPrerequisite in our Continuous Deployment model: deploy every green merge safely Per-tenant enablement for B2B without hotfix releases Instant rollback by flipping a flag, not rebuilding images Why still assess elsewhere:\nRoll-your-own adds branching and cleanup debt Long-lived flags become dead code; need expiry discipline On-prem or single-tenant deliverables may prefer Continuous Delivery with manual promote only Implementation options:\nApproach Notes Managed LaunchDarkly, Unleash, Flagsmith, ConfigCat (SaaS or self-hosted) OpenFeature Vendor-neutral SDK surface; pick a provider backend Config + DB Simple boolean table; fine for one product, watch ops burden Env/config only Not flags; redeploy to change (avoid for user-facing toggles) Details Topic Notes Naming feature_flags, toggles; consistent prefix in code Defaults Safe default off for new features; document in runbooks Lifecycle Ticket to remove flag and dead branches after full rollout Testing CI matrix: flag on/off; avoid tests that only pass one state Security Admin flags are authorization; audit changes like RBAC Observability Log evaluation or exposure events for experiment analysis Garden pattern: trial when Software as a Service + Continuous Deployment; assess for internal tools until CD maturity is proven. Pair with metrics and rollback, not as a substitute for tests.\nNot the same as: Policy as Code (infra compliance); GitOps sync (delivery mechanism); environment-specific config files without runtime evaluation.\nReferences\nFeature Toggles (Martin Fowler) OpenFeature ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Feature Flags"},"/garden/first-touch-provisioning":{"content":"First Touch Provisioning\nFirst touch provisioning creates the foundation layer of infrastructure: cloud accounts and guardrails, networks, DNS, IAM roles, clusters, VMs, databases as managed services, and remote state. We adopt it via Declarative IaC (Terraform / OpenTofu). second touch provisioning configures what runs on that foundation (packages, agents, app config); prefer immutable images and GitOps over repeated SSH configuration.\nBlurb In computing, provisioning means to provide or equip an information technology system with products or services required to make it operational.\nSummary First touch (typical artifacts):\nLayer Examples Identity / org AWS accounts, GCP projects, Azure subscriptions, org policies Network VPC/VNet, subnets, firewalls, load balancers, private DNS Compute platform EKS/GKE/AKS cluster, autoscaling groups, serverless runtimes Data plane (managed) RDS, Cloud SQL, S3/GCS buckets with policies Ops plumbing Remote state buckets, CI OIDC roles, observability sinks Second touch (separate technique, often assess):\nAnsible playbooks on existing VMs cloud-init / startup scripts that drift from IaC In-cluster Helm / manifests after the cluster exists When first touch is enough:\nKubernetes: cluster + IAM + networking in Terraform; workloads via GitOps Serverless: functions, queues, tables defined declaratively; no SSH config loop Cattle Not Pets: golden images built in CI, not hand-provisioned servers When you need both touches:\nBrownfield VMs imported into the cloud Legacy middleware Ansible still owns until replaced by containers Split responsibility: platform team owns first touch, app team owns Helm/GitOps (still two phases, different owners) Details Topic Notes Tools Terraform (adopt); avoid new Imperative IaC / Pulumi generators for greenfield State Remote backend per env; locking; no local-only state for shared infra Modules Reusable VPC/cluster modules; watch blast radius (DRY discipline) Policy Policy as Code on plans before apply Provisioner anti-pattern Terraform remote-exec / heavy local-exec blurs touches; keep first touch declarative Garden pattern: adopt first touch as code for every Cloud estate; assess second touch only where immutable/git-native paths are not ready. See Provisioner for how tools map to each phase.\nReferences\nWikipedia: Provisioning ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"First Touch Provisioning"},"/garden/framework":{"content":"Under Code, Framework groups opinionated application stacks that own project shape: routing, lifecycle, dependency injection, ORM conventions, and “where code goes.” You trade flexibility for speed. Prefer Library composition unless the framework’s opinions match your product for years.\nIn this subcategory:\nItem Rating Use when Ruby on Rails hold Maintaining legacy Rails; not default for greenfield Related subcategories (do not duplicate tags):\nSubcategory Distinction Library You call it; it does not own the app skeleton (OpenTelemetry, Zap) Test Framework Exists to run/assert tests (TestContainer, Helm Unittest) game engine Real-time interactive runtime (player loops, not CRUD web) Language Syntax/spec only (YAML, OpenAPI) What belongs here:\nWeb/app frameworks: Rails, Spring, Django, ASP.NET, Next.js (when rated as a Code dependency) Full-stack runtimes that dictate MVC (or similar) layout Items tagged subcategories: [\"[Framework](/garden/framework/)\"] on Code tools What does not belong here:\nDesign Pattern (technique-level patterns, not a product) Declarative IaC / cloud “frameworks” (Terraform modules are not app frameworks) Agent Skills Framework and similar Technique names (portable skill folders, not app code) Bare UI kits or single-purpose libs (use Library) Test runners (use Test Framework) Garden stance:\nDefault: small Library graph + boring Language; add a framework only when conventions clearly beat bespoke structure. Hold heavy monolith frameworks (Ruby on Rails) for new products unless the team and estate are already committed. Assess niche frameworks (e.g. Ebitengine belongs under game engine, not general web Framework). Framework upgrades are migration projects: pin versions, budget escape hatches (strangler, service extraction). How to tag: category: \"[Code](/garden/code/)\" and subcategories: [\"[Framework](/garden/framework/)\"] when the artifact is an opinionated app platform you build inside, not a CLI you operate (Tool) or infra you deploy (Platform).","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Framework"},"/garden/frontegg":{"content":"Frontegg is a B2B user-management platform: embedded auth, tenant/org modeling, admin portals, roles, audit logs, and product-facing identity UX; not just OIDC plumbing. We rate it assess alongside Auth0 when building multi-tenant Software as a Service and you want opinionated B2B flows without operating Keycloak-class infrastructure yourself.\nBlurb Frontegg is the user management platform for modern SaaS: authentication, authorization, and everything in between.\nSummary Vs Auth0: Auth0 is the general-purpose embedded IdP; Frontegg optimizes for B2B product surfaces (customer admins, invitations, tenant settings, embeddable components). Pick Frontegg when “user management as a product feature” dominates; pick Auth0 when you mainly need standards-based login and will build tenant UX yourself.\nWhen to assess: B2B SaaS with per-customer admins, self-service onboarding, and compliance-friendly audit trails; teams that want hosted UI/SDK rather than custom screens on raw OIDC APIs.\nWhen to skip: simple internal apps, single-tenant tools, or shops already standardized on workforce IdP + minimal app auth; strict build-it-all-in-house policies.\nOps: same criticality as any IdP, MFA for vendor admin, least-privilege API keys, DevSecOps review of webhooks and embed config, map vendor roles to app RBAC.\nDetails Topic Notes Protocols OIDC, OAuth 2.0, SAML (enterprise SSO connections) Product Embeddable admin portal, tenant isolation, invitations Pricing MAU/tenant tiers; model before commit Exit Prefer standards at integration boundaries to limit lock-in ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"FrontEgg"},"/garden/game-engine":{"content":"A game engine is a specialized Framework for interactive software: rendering, input, audio, physics, scene graphs, and asset pipelines so teams ship games instead of rebuilding low-level loops. Engines range from editor-driven stacks (Unity, Godot, Unreal) to code-first libraries such as Ebitengine for GoLang.\nUse this subcategory for runtime/tooling aimed at players and real-time loops; not general app frameworks or graphics libraries alone.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Game Engine"},"/garden/gatekeeper":{"content":"Gatekeeper is the Kubernetes admission controller for Open Policy Agent (OPA): validating and mutating webhooks plus audit scans, driven by Rego ConstraintTemplates and Constraints (CRDs). We rate it assess: use for in-cluster enforcement when Policy as Code rules need Rego at admission time; prefer Conftest (trial) on PRs first and CEL for simple native ValidatingAdmissionPolicy rules before operating a full OPA stack.\nBlurb Gatekeeper is a validating and mutating webhook that enforces CRD-based policies executed by Open Policy Agent, a policy engine for Cloud Native environments hosted by CNCF as a graduated project.\nSummary Where it runs:\nLayer Tool When Pre-deploy (CI) Conftest Test manifests, Helm output, Terraform JSON on Pull Request Admission (cluster) Gatekeeper Block or mutate creates/updates at the API server Admission (native) CEL Narrow rules without OPA ops (K8s 1.26+) Audit (cluster) Gatekeeper Report existing resources violating policy What you get:\nConstraint library (community templates: labels, images, security context) Mutation (patch resources to comply, use carefully) External data (call out to systems during policy eval) Shared Rego with Conftest when policies are designed for both paths Why assess (not default adopt):\nController HA, webhook latency, and upgrade coupling to Kubernetes version Rego authoring and debugging cost (same as OPA elsewhere) Overlap with Kyverno (YAML policies) and CEL for teams that do not need full Rego When to assess:\nMulti-tenant clusters with mandatory guardrails (PSA, labels, disallowed registries) Need audit mode to find drift on live objects Already trialing Conftest and want one policy language end to end When to skip:\nNo Kubernetes (use Conftest / Regula on IaC only) Simple deny rules solvable with CEL and no extra controllers Greenfield without policy maturity (start with CI gates, not admission complexity) Details Topic Notes Install Manifest or Helm chart (gatekeeper-system namespace) Policies Start from Gatekeeper policy library GitOps Constraints in Git; ArgoCD syncs like app manifests Performance Watch webhook timeouts; scope constraints to needed namespaces Mutation Higher blast radius than validate-only; test in dry-run/audit first Garden pattern: adopt Policy as Code as a technique; trial Conftest in CI; assess Gatekeeper when cluster admission must enforce the same Rego family. Compare CEL before committing to OPA operations.\nReferences\nGatekeeper documentation OPA ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Gatekeeper"},"/garden/gbrain":{"content":"gbrain is Garry Tan’s opinionated pattern for an LLM-maintained personal knowledge base. We rate it trial: the ideas are worth stealing (especially two-layer pages and MECE directory resolvers) but the full package is prescriptive (agent-maintains-everything, enrichment on every signal). Cherry-pick patterns and adapt them to your vault and workflows; do not treat gbrain as a religion.\nBlurb A personal intelligence system where your AI agent builds and maintains an interlinked wiki of everything you know (people, companies, deals, projects, meetings, ideas) as structured, cross-referenced markdown files. The agent writes and maintains all of it. You direct, curate, and think.\nSummary Worth applying: pre-computed synthesis above a --- separator with an append-only Timeline below; directory README.md resolvers that say what belongs where; one primary home per entity with wiki-links for adjacency.\nTake with skepticism: blanket “enrichment fires on every signal,” full automation of maintenance, and any rule that does not match how you file work (e.g. this vault uses PARA zones plus garden notes, not a single gbrain tree).\nThis vault: we adopt the two-layer pattern for research and garden notes (Agent Skills, tech-garden items); we assess a root RESOLVER.md and always-on enrichment pipelines before copying them wholesale.\nPortable patterns (not dogma) Two-layer pages (compiled truth + timeline) Above ---: current synthesis (rewrite when facts change. Below ---: append-only edit log (**YYYY-MM-DD** | Author) …). Subject-matter history belongs above the line; the timeline records document changes only.\nMECE directories (light touch) Resolvers in zone README.md files help agents and humans file consistently. MECE applies to directories, not reality. Use cross-links when entities span domains.\nEnrichment on every signal (optional) Powerful for high-touch CRM-style brains; optional here. Prefer explicit jobs (Dagu, morning briefing) over implicit always-on writes.\nPage template (adapt as needed) 1 2 3 4 5 6 7 8 9 10 11 12 # Entity Name > Executive summary , current state in one paragraph. ## State - **Key field:** value ## Open Threads - Active items (resolve → move detail to Timeline) ## See Also - Linked page ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"gbrain"},"/garden/gemini":{"content":"Gemini CLI is Google’s open-source agentic coding agent for the terminal, ReAct loop, built-in tools (files, shell, search), MCP servers, and large-context Gemini models. We rate it trial under AI Agent: a solid choice on Google accounts or Gemini API when you want Google’s stack and Agent Skills portability; we usually default to Cursor + cursor-agent for daily editor work and Claude Code / Codex for other vendor stacks.\nBlurb Gemini CLI is an open-source AI agent that brings Gemini directly into your terminal. It helps you build, debug, and automate development workflows with built-in tools and MCP support.\nSummary Gemini CLI (google-gemini/gemini-cli) runs an agent loop in your repo: reason, call tools, checkpoint conversations, and load project context via GEMINI.md. It shares quota with Gemini Code Assist agent mode on free and paid Google tiers.\nUse for interactive coding, refactors, and skill-driven workflows when Gemini models or Google Cloud billing are the constraint. It is a repo-bounded AI Agent; not an omnichannel personal bot (OpenClaw, Hermes are hold). For scheduled automation, prefer deterministic scripts and Dagu with LLM steps only where reasoning is required.\nCompare Claude Code (Anthropic) and Codex (OpenAI) for the same agent-loop-in-repo pattern.\nDetails Install: npm install -g @google/gemini-cli, Homebrew (brew install gemini-cli), or npx from GitHub; Node.js 20+. Auth: Google account (free tier quotas) or Gemini API key for pay-as-you-go. Models: Gemini 2.5 Pro and successors; very large context windows per Google docs. Skills: SKILL.md / Agent Skills ecosystem, see Agent Skills - Sources for cross-agent collections. Fit: Tool / AI Agent, agent-loop coding tool (also branded Gemini Code Assist in IDE surfaces). Contrast: Cursor + cursor-agent for default IDE workflow; Claude Code / Codex on other vendors. ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Gemini"},"/garden/genesis":{"content":"Genesis is a Claude Code meta-project: you run Genesis inside Claude, describe a new app (stack + name), and it scaffolds a sibling repo with CLAUDE.md, .claude/agents/, .claude/skills/, hooks, .mcp.json, memory seeds, and starter code. We rate it assess: worth a pilot when bootstrapping greenfield Claude-native repos; this vault already hand-rolls the same Agent Skills patterns.\nBlurb The Claude that builds Claudes. Bootstrap fully-equipped Claude Code projects in under two minutes.\nSummary Problem it solves: productive Claude Code needs upfront investment (project rules, agents, skills, hooks, MCP, permissions). Most teams ship a thin CLAUDE.md and underuse subagents and skills.\nFour-phase workflow:\nPhase Output Interview 1-4 clarifying questions Plan Agents, skills, MCP list, folder layout for approval Generate Full project tree in one pass Finalise Git init, registry entry, next steps Generated artifacts (typical):\nCLAUDE.md with stack-specific standards .claude/agents/ (test-runner, reviewer, domain agents) .claude/skills/ (/test, /lint, /review, /commit + domain skills) .claude/settings.json (permissions, format hooks) .mcp.json, memory files, app boilerplate, .gitignore Supported stacks: Node/TS, Python, Go, Rust, Ruby, Java/Kotlin (profiles include linters, tests, folder layout).\nWhen to assess:\nStarting many similar Claude Code repos and want consistent scaffolding Teaching teams the Agent Skills layout by example You already trial Claude Code and want faster first-session productivity When to skip:\nPrimary workflow is Cursor / cursor-agent (Genesis is Claude Code-specific) Monorepo with established agent conventions (merge by hand, do not overwrite) You only need a single skill file, not a full project factory Details Topic Notes Prerequisites Claude Code CLI, git, Node 18+; bash/zsh/PowerShell Install Clone to e.g. ~/claude/genesis, run claude in that directory Skills /genesis, /registry, /validate, /update inside Genesis First run Writes personalisation.md + environment.md (gitignored) Security Review generated code and MCP configs; MIT license with disclaimer Garden pattern: assess as a bootstrap accelerator, not a runtime. Pair with Agent Skills - Sources for skill inspiration; keep vault agent-skills/ as the canonical pattern for this repo.\nReferences\nGenesis (GitHub) Documentation ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Genesis"},"/garden/git":{"content":"Git is the default distributed version control system for every repo we touch: local branches, cheap merges, and a full history on each clone. We adopt it as the baseline under Pull Request / GitOps workflows on GitHub or GitLab; use git lfs when binaries or large assets would bloat history.\nBlurb Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.\nSummary Core model: commits point at snapshots of the tree; branches are movable refs; remotes sync packs of objects. Each developer has a full copy of history locally, so most work (branch, commit, diff) needs no network.\nDay-to-day loop:\nStep Command / concept Clone / fetch git clone, git fetch, git pull Branch cheap local branches vs linear central VCS Worktree checked-out files you edit Stage index / staging area (git add, git restore) Commit immutable snapshot in the object store Share git push to GitHub, GitLab, or other remote Why adopt over SVN / Perforce / ClearCase:\nBranching and merging are normal, not exceptional Staging lets you craft commits before recording history Distributed workflow: review offline, push when ready Ecosystem: GitHub Actions, GitOps, Policy as Code, and every Internal Developer Platform assume Git Pair with: .gitattributes (EOL normalization; complements EditorConfig), signed commits where policy requires, and git lfs for large blobs.\nDetails Under the hood (practical mental model): Git stores content-addressed objects (blobs, trees, commits) in an append-only object database. The index tracks what the next commit will contain; the working tree is your checkout on disk. You do not need to master plumbing to use Git well, but knowing “commit = snapshot + parent pointer” explains rebases and merge results.\nMerge philosophy: Git reconciles toward a consistent working tree, not strict chronological file history. It replays or combines commits so the checkout matches intent; when two sides change the same lines irreconcilably, you get a merge conflict with conflict markers. You edit the merged file, git add, commit, and push so teammates inherit the resolution. Centralized tools that enforce linear locks on paths tend to conflict more often because they privilege checkout order over current tree state.\nCommon commands (reference):\nArea Examples History git log, git show, git blame Undo git revert, git reset (know soft/mixed/hard) Sync git pull --rebase, git merge, git rebase Hygiene git stash, git clean, git gc Team practices: trunk-based or short-lived branches; Pull Request as the review gate; protected main; no force-push to shared branches without agreement. For infrastructure and config, treat the repo as source of truth (GitOps).\nNot the same as: GitHub / GitLab (hosting + collaboration); GitOps (delivery pattern); git lfs (large file extension).\nGarden pattern: adopt Git for all new code; default hosting is org GitHub unless a customer mandates GitLab.\nReferences\nGit Pro Git book ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Git"},"/garden/git-lfs":{"content":"Git LFS is an open-source git extension that stores large blobs (binaries, media, datasets) outside the object database and keeps lightweight pointer files in history. We adopt it whenever checked-in assets would bloat clones or make git log unusable—typical on GitHub or GitLab repos with images, models, or build artifacts that must stay versioned.\nBlurb Git Large File Storage (LFS) replaces large files such as audio samples, videos, datasets, and graphics with text pointers inside Git, while storing the file contents on a remote server like GitHub.com or GitHub Enterprise.\nSummary Problem: Git is optimized for text diffs; committing multi‑MB or GB files duplicates full blobs in every revision and slows clone/fetch for everyone.\nWhat LFS does: On commit, matching paths are uploaded to LFS storage and replaced in the tree with small pointer files. On checkout, hooks swap pointers back to real files. Server-side, LFS stores file contents separately from the Git packfiles.\nWhen to use: repos with PSDs, videos, ML weights, firmware images, or other binaries you need in VCS—not in object storage alone.\nWhen to skip: generated artifacts that should never be committed; assets better served from a registry or CDN; repos where git lfs is not enabled on the host (fix hosting first).\nPair with: git as the base VCS; .gitattributes patterns via git lfs track; git lfs migrate to rewrite history when adding LFS to an existing repo.\nDetails Setup (once per machine):\n1 git lfs install Per repository:\nTrack patterns: git lfs track \"*.psd\" (writes/updates .gitattributes) Commit .gitattributes with the patterns Add and commit large files normally; git push uploads LFS objects to the remote LFS API Mental model:\nLayer Behavior Working tree Real file bytes you edit Git commit Pointer file in the tree for tracked extensions LFS server Content-addressed blob storage (GitHub/GitLab/self-hosted) History migration: Tracking in .gitattributes does not retroactively convert old commits. Use git lfs migrate import (or related subcommands) when adopting LFS on a repo that already committed large files.\nHosting: GitHub and GitLab ship LFS endpoints and quotas; self-hosted Git needs an LFS-compatible server. Same branch protections and access controls apply to LFS objects as to the rest of the repo.\nOperational notes: clones need the LFS CLI installed or fetches omit real file content; CI runners must install git-lfs and run git lfs pull where needed; monitor LFS bandwidth/storage quotas on SaaS hosts.\nSecurity: keep the client updated (e.g. 3.7.1+ for known advisories).\nNot the same as: submodule-only binary repos without LFS; artifact registries (npm, OCI) for release binaries; git itself.\nReferences\nGit LFS git-lfs/git-lfs documentation ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"git lfs"},"/garden/git-worktree":{"content":"Git Worktree\nGit worktrees let one git repository hold several checked-out branches at once, each in its own directory. We trial the technique when parallel work beats stashing or re-cloning: feature branch plus PR review, hotfix while main stays clean, or agent tooling that needs an isolated checkout per branch.\nBlurb Manage multiple working trees attached to the same repository. A git repository can support multiple working trees, allowing you to check out more than one branch at a time.\nSummary What it is: A repo has one main worktree (from git clone or git init) and zero or more linked worktrees. Each worktree shares the same object database but has its own HEAD, index, and files on disk. Git tracks metadata under .git/worktrees/.\nWhen to use:\nSituation Why worktrees help Feature + review Keep main checked out while you review a Pull Request in another folder Hotfix interrupt Branch off production without disturbing in-progress edits Parallel agents Tools like Cursor best-of-N runners can use one worktree per attempt Long builds Run tests in one tree while you edit in another When to skip: Single-branch solo work with no context switching. Repos where disk space is tight (each tree duplicates the working copy). Teams that prefer one clone per branch in CI only.\nCore commands:\nCommand Purpose git worktree add ../path branch New linked tree at ../path on branch git worktree add -b new-branch ../path Create branch and check it out git worktree list Show all trees, paths, and checked-out refs git worktree remove ../path Drop a linked tree (branch stays) git worktree prune Clean stale admin files after manual deletes Rules of thumb: One branch per worktree at a time (Git blocks duplicate checkouts). Run add from the main repo; put sibling paths outside the main tree (../feature-x) to avoid nested folders. Remove linked trees when done so .git/worktrees stays tidy.\nPersonal Experience The reason this is a trial vs adopt is because it works different than normal git clone. IDEs can get confused, and they don’t always play nice with devcontainers, so your mileage may vary.\nOur main usage is when we have to work on multiple branches at the same time (for example: main, staging, prod). In this case we structure things in a very specific way:\nCreate a directory to contain the worktrees mkdir <repo name> cd <repo name> Clone the main worktree git clone <repo url> main Create all other worktrees as siblings cd main git worktree add ../staging staging git worktree add ../prod prod Use the correct directory / worktree for your work Details Versus alternatives:\nApproach Trade-off git stash Fast, but only one checkout; easy to lose context in a deep stash stack Second git clone Fully isolated, but doubles fetch/storage and drift between clones Worktree Shared objects, one remote config; must respect one-branch-per-tree rule Detached HEAD trees: git worktree add --detach ../experiment gives a throwaway checkout for spikes or reproducing a bug at a specific commit without creating a branch.\nLock and move: git worktree lock prevents accidental removal (useful on shared machines). git worktree move relocates a tree if you reorganize directories.\nCleanup: If you delete a worktree folder by hand, run git worktree prune. Use git worktree repair after path moves or corrupted admin metadata.\nGarden stance: Trial on any repo where you already adopt git and regularly switch branches mid-task. Promote to adopt once worktrees are part of your default local workflow.\nReferences\ngit-worktree Pro Git: Git Tools - Worktrees ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-28T00:00:00-04:00","tags":null,"title":"Git Worktree"},"/garden/github":{"content":"GitHub is the default Platform for hosting git repos, collaboration, and the surrounding delivery toolchain. We adopt it as org-standard forge unless a customer or compliance boundary requires GitLab—then mirror the same practices (Pull Request, protected main, GitHub Actions or equivalent CI, GitOps from the default branch).\nBlurb The complete developer platform to build, scale, and deliver secure software.\nSummary What it is: SaaS (or GitHub Enterprise) for Git remotes, issues, discussions, releases, and integrations—plus first-party products (Actions, Packages, Codespaces, Advanced Security) on top of the same repo graph.\nWhy adopt: largest ecosystem (marketplace actions, OIDC to cloud, Dependabot, branch protection APIs); default for OSS and most vendors; GitHub Actions and Policy as Code / scanner apps assume GitHub as the control plane.\nWhen to use: new org or product repos; public or private code; PR-based Code Review; CI/CD via GitHub Actions; git lfs for large assets; GitHub Pages or release assets when appropriate.\nWhen to use GitLab instead: customer mandate, air-gapped self-managed GitLab, or all-in-one DevOps on GitLab CI without Actions.\nNot the same as: git (the VCS protocol and CLI); GitHub Actions (CI/CD only); GitOps (deploy pattern); GitLab (competing platform).\nDetails Capability Garden link / note Version control git remotes; forks; default branch policies Review & merge Pull Request, required reviewers, CODEOWNERS CI/CD GitHub Actions (.github/workflows/) Large files git lfs (hosting quotas apply) Delivery pattern GitOps—repo as truth; Actions build, cluster tools sync Security Dependabot, secret scanning, branch protection; pair with Shift Left / DevSecOps checks Org practices we expect:\nRepos under the org (not personal accounts) for team work Protected main / release/*; no direct pushes; required status checks Least-privilege GitHub Apps or fine-grained PATs; prefer OIDC from Actions to cloud over long-lived keys .github org templates for workflows, issue forms, and security policy where it helps consistency Document exceptions when a repo must live elsewhere (customer fork, mirror, legacy host) Enterprise / compliance: GitHub Enterprise Cloud or Server when SSO, audit log retention, or data residency requires it; align with customer identity (SAML/OIDC) and IP allow lists as needed.\nGarden pattern: adopt GitHub for new work; GitLab only by explicit requirement; keep git skills portable between hosts.\nReferences\nGitHub GitHub Docs About GitHub ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"GitHub"},"/garden/github-actions":{"content":"GitHub Actions is GitHub’s built-in CI/CD engine: event-driven workflows defined in .github/workflows/*.yml, executed on GitHub-hosted or self-hosted runners. We adopt it as the default CI-CD Tools plane for any repository on GitHub; pair cluster delivery with ArgoCD (build and push in Actions, sync in Argo) rather than imperative deploy scripts.\nBlurb Automate, customize, and execute your software development workflows right in your repository.\nSummary Role: Continuous Integration on every push and pull request (build, test, lint, security scans); optional Continuous Delivery steps (artifacts, releases, environment promotions) without leaving the repo.\nWhen to use: code lives on GitHub; you want branch protection, required checks, and workflow-as-code next to the application; you need marketplace actions, reusable workflows, or org-wide workflow templates.\nWhen to skip: primary forge is GitLab (use GitLab CI instead); heavy Kubernetes-native pipeline CRDs are a deliberate platform choice (Tekton, assess); long-lived pet CI servers (Jenkins, hold) when Actions plus OIDC can replace them.\nPairs with: Shift Left and DevSecOps gates on the PR path; Policy as Code and third-party scanners (e.g. Codacy, assess) as required checks; cursor-agent for headless agent jobs in workflows when you are on Cursor billing.\nNot the same as: ArgoCD (GitOps deploy controller for Kubernetes); Argo Workflows (in-cluster DAG workflows). Actions runs the pipeline; Argo CD reconciles desired cluster state after merge.\nDetails Topic Notes Workflows Triggers: push, pull_request, schedule, workflow_dispatch, labels, releases Reuse Callable workflows and composite actions; pin third-party actions to full SHAs Runners ubuntu-latest for most jobs; self-hosted only when hardware, network, or compliance requires it Secrets Repo/org secrets and environments; prefer OIDC (id-token: write) to cloud roles over static cloud keys in YAML Permissions Least-privilege permissions: at workflow or job level; avoid blanket contents: write Environments Protection rules and required reviewers for staging/production deploy jobs Artifacts & caches Pass build outputs between jobs; cache dependencies with stable keys Concurrency concurrency groups to cancel superseded PR runs and avoid deploy races Practices we expect: required status checks on main; no secrets in logs; dependabot or renovate for action and dependency bumps; separate workflows for PR validation vs release/deploy.\nReferences\nGitHub Actions documentation Security hardening OpenID Connect ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"GitHub Actions"},"/garden/github-next-repo-visualization":{"content":"GitHub Next repo visualization is an experimental GitHub project that graphs codebase structure and dependencies for exploration in the browser. We assess it for onboarding and architecture reviews; production docs still flow through Diagramming and the vault graph.\nBlurb Explore your repository as an interactive map of files and relationships.\nSummary When to use: large unfamiliar repos; demos for stakeholders; comparing module coupling before a refactor.\nWhen to skip: need committed diagrams in git (Mermaid, Draw.io); air-gapped code that cannot call GitHub APIs; long-term support requirements (experiment may stall).","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"GitHub Next Repo Visualization"},"/garden/gitlab":{"content":"GitLab is an all-in-one DevSecOps Platform for git hosting, merge requests, built-in CI/CD, registry, and security scanning. We rate it trial at org level because GitHub is the default. Use GitLab when a customer, regulator, or air-gapped deployment requires it, then apply the same discipline (Pull Request / MR review, protected default branch, pipeline gates, GitOps from main).\nBlurb Your intelligent orchestration platform for DevSecOps — from planning to source code management to CI/CD, everything you need to build and ship software faster in one platform.\nSummary What it is: SaaS GitLab.com or self-managed GitLab / Dedicated with a single application for repos, issues, wiki, CI (.gitlab-ci.yml), container/package registry, and integrated SAST/SCA/secret/DAST in merge requests.\nWhy trial (not default): GitHub has the broader marketplace, our existing GitHub Actions investment, and typical vendor integrations; GitLab wins on consolidated DevSecOps UI, self-hosted/air-gap, and customer mandates.\nWhen to use: customer hosts on GitLab; compliance needs self-managed or isolated instance; team standardizes on GitLab CI and built-in security scanners instead of Actions plus third-party apps.\nWhen to stay on GitHub: greenfield org repos, OSS publishing, and stacks that assume GitHub OIDC, Actions, or Dependabot.\nNot the same as: git (VCS); GitHub (default platform); GitHub Actions (GitHub-only CI); GitOps (delivery pattern).\nDetails Capability Notes Version control git remotes; forks; protected branches Review Merge requests (same role as Pull Request on GitHub) CI/CD GitLab CI pipelines in .gitlab-ci.yml; includes, rules, environments Registry Container and package registry in-platform Security SAST, dependency scanning, secrets, DAST in MR; DevSecOps / Shift Left alignment Large files git lfs supported; quota per tier Self-hosted Common for public sector, telecom, and air-gap Practices when GitLab is required:\nMirror GitHub norms: no direct pushes to main; required pipeline success; CODEOWNERS / approval rules Prefer CI/CD variables and OIDC/job tokens over long-lived deploy keys where available Use merge request templates and security scan results in the MR, not email-only review Keep git workflows portable—branch naming, conventional commits, and GitOps repos work the same; only the forge and CI YAML differ GitLab vs GitHub (quick map):\nGitHub GitLab Pull request Merge request GitHub Actions GitLab CI GitHub Packages GitLab registry Dependabot Dependency scanning / Renovate-style bots Garden pattern: trial — choose GitLab only with a documented reason (customer, compliance, existing estate); otherwise adopt GitHub for new repos.\nReferences\nGitLab GitLab Docs About GitLab ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"GitLab"},"/garden/gitops":{"content":"GitOps\nGitOps 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. ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"GitOps"},"/garden/go":{"content":"GoLang\nBuild simple, secure, scalable systems with Go\nSearch tip\nUse “GoLang” when searching , “Go” is too generic to get useful results.\nGo is a language Google created to handle its unique scaling needs. Designing for those needs eliminates the performance and scale cliff that almost everyone eventually hits. Because the language itself is fairly simple, it has become the defacto language for DevOps tooling , anywhere large-scale and/or highly concurrent programming is needed.\nOne of its core strengths is an active community of maintainers and a Special Interest Group (SIG) that requires a working reference implementation before anything can be added to the language. Unlike many languages where a theoretical feature is added only to be poorly thought out, the SIG process forces a working fork implementation before a feature can be merged. This makes Go move slowly, but the solutions are almost always correct and well-considered.\nThis can frustrate developers familiar with other languages , package management, inheritance, and generics all look different in Go. These are strengths, not weaknesses.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"GoLang"},"/garden/go-script":{"content":"go script\nThe go script pattern (often ./go at repo root) is a small GoLang program that replaces Make for project tasks: build, test, lint, and release with one cross-platform entrypoint. We adopt it for repos where Make is painful on Windows and shell scripts sprawl across OSes.\nBlurb A single Go file at the root of your repository can act as a portable task runner with typed logic, subcommands, and access to the full Go standard library.\nSummary How it works: developers run ./go test or go run ./go test (depending on shebang and execute bits). Subcommands map to functions; flags use flag or cobra if the script grows.\nWhen to use: polyglot teams need identical commands on macOS, Linux, and Windows; tasks need real code (API calls, code gen) not just shell pipelines.\nWhen to skip: the repo is already standardized on Dagu, Nx, or a monorepo tool; a thin Makefile plus documented scripts is enough.\nReference: ThoughtWorks write-up linked in url; pair with git hooks or GitHub Actions calling the same ./go targets in CI.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"go script"},"/garden/go-template":{"content":"Go Template\nIf 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.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Go Template"},"/garden/gocd":{"content":"GoCD is an open source continuous delivery server from ThoughtWorks. It models pipelines, stages, and artifacts with explicit promotion between environments. We assess it alongside Jenkins for legacy CD installs; new pipelines should not start here unless migration cost dominates.\nBlurb GoCD is an open-source tool which is used in software development to help teams and organizations automate the continuous delivery of software.\nSummary When to use: maintaining an existing GoCD server; need visual pipeline DAG and artifact promotion semantics teams already understand.\nWhen to skip: greenfield repos; preference for pipeline-as-code in git without a central CD server; GitOps models where cluster state is the source of truth.\nNote: operational burden similar to other pet CD servers; treat the server as infrastructure with backups and upgrades planned.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"GoCD"},"/garden/goodcheck":{"content":"goodcheck is a pattern-based code checker from Sider (formerly SideCI). You write rules in YAML to flag risky or discouraged code patterns in Pull Requests and local runs. The GitHub repo is gone and the vendor is defunct. We rate it hold: useful ideas, but not a living project to adopt for new work.\nStatus Abandoned, not renamed. Sider ended its code-review service in September 2022. The company (株式会社 Sider) completed liquidation on 2025-03-04. github.com/sider/goodcheck and the earlier sideci/goodcheck both return 404. There is no official successor repo or maintained fork with comparable traction.\nBlurb goodcheck is a customizable checker for source code. You can write your own rules in YAML.\nSummary When it was useful: enforce house style (“do not call X”, “prefer Y over Z”) without standing up a full SAST platform; fast feedback in CI.\nWhen to skip now: greenfield adoption. Prefer Semgrep-class rule runners or language-native linters with active maintenance.\nStill available (frozen): Ruby gem goodcheck 3.1.0 on RubyGems (last release 2021-07-15). Docs at sider.github.io/goodcheck. Docker image sider/goodcheck appears removed from Docker Hub.\nAlternatives: Semgrep, custom grep/regex CI steps, Conftest / OPA for policy on IaC (not a drop-in for app-source YAML rules).\nDetails History Era Org / repo Notes 2014-2018 SideCI Tokyo-based automated PR review product 2018+ Sider (sider/goodcheck) Rebrand; goodcheck open-sourced as AGPL Ruby gem 2022-09 Service shutdown sider.review end-of-service (see Web Archive) 2025-03 Company liquidation 株式会社 Sider wound up; GitHub org repos deleted Rule shape (for comparison) Rules are YAML lists with id, pattern (regex), message, optional glob, pass/fail examples. Same niche as lightweight custom Semgrep rules, but with a smaller ecosystem.","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"goodcheck"},"/garden/graphql":{"content":"GraphQL\nBlurb The query language for modern APIs.\nSummary Published by Facebook in 2015, it solves a problem that is unique to them that really only existed for a short period of time. Namely, requests were coming in so fast that it was wasting a lot of bandwidth, since not all clients needed the same information.\nOpenAPI is a much lighter weight solution, which solves far more use-cases, until you really do hit the hyper-scale of Facebook. Until you do it is best if you avoid GraphQL.","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"GraphQL"},"/garden/grpc":{"content":"gRPC\nBlurb A high performance, open source universal RPC framework\nSummary gRPC suffers the same issue as general RPC. That is it is a fundamentally flawed approach to cross computer service communication. However, there are some IPC use cases where it makes sense; like terraform's provider.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"gRPC"},"/garden/harness-io":{"content":"Harness is a commercial CI/CD and software delivery platform. It covers pipelines, GitOps, feature flags, cloud cost, and security testing in one control plane. We assess it when an enterprise wants a packaged CD story; default build/deploy paths remain GitHub Actions or GitLab unless sales or compliance drives Harness.\nBlurb Harness is a modern software delivery platform that automates CI/CD, enables GitOps, and embeds security and cost controls across the SDLC.\nSummary When to use: centralized governance for many teams; need vendor support for regulated industries; evaluating feature flags and deployment governance in one SKU.\nWhen to skip: cost-sensitive startups; teams already standardized on Actions plus ArgoCD; preference for composable OSS over suite pricing.\nPairs with: existing Kubernetes clusters, secret managers, and policy tools (Policy as Code, Conftest) rather than replacing them.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Harness.io"},"/garden/hcl":{"content":"HCL\nHashiCorp Configuration Language (HCL) is a human-readable DSL for structured configuration. It is the native language of Terraform, and also used by Packer, Nomad, Vault, and historically Vagrant. HCL predates YAML’s dominance in Declarative IaC and remains the right choice when you are committed to the Terraform or OpenTofu toolchain; not as a general-purpose alternative to YAML for Kubernetes manifests or Docker Compose.\nRated trial: still the practical default inside HashiCorp-style IaC, but the 2023 BSL license change and growth of YAML-first and CDK-style tools (Pulumi) mean you should confirm ecosystem fit before new platform bets, not adopt HCL in isolation.\nBlurb HCL (HashiCorp Configuration Language) is a configuration language built by HashiCorp. HCL is designed to be both human- and machine-friendly, for use with command-line tools, but specifically targeted towards DevOps tools, servers, and configuration files.\nSummary HCL grew out of a practical constraint: when HashiCorp moved its tools to GoLang, embedding Go as an end-user DSL was awkward, so they built a small declarative language tuned for blocks, attributes, and references, patterns that map cleanly to infrastructure resources. It is intentionally not Turing-complete in the same way as a general programming language; expressions and functions exist, but the model stays declarative, which keeps diffs reviewable and state plans predictable.\nFor greenfield work outside Terraform/OpenTofu, YAML (plus optional templating via Helm or YAMLScript) or imperative/semantic IaC via Pulumi often wins on hiring surface area and license clarity. Inside an existing Terraform or OpenTofu estate, learning HCL is non-optional.\nDetails Syntax families: Native HCL (.tf, .hcl), JSON syntax as an alternate encoding for the same Terraform schema, and HCL2 as the current generation used by modern Terraform. Ecosystem: Tied to Terraform; OpenTofu maintains compatible HCL. Other HashiCorp products consume HCL-shaped config but are separate adoption decisions. License context: HashiCorp’s BSL shift does not change HCL’s technical role but affects how aggressively to standardize on HashiCorp-only stacks versus forks or YAML-native tools. When to prefer alternatives: Kubernetes and cloud-native config (YAML), cross-cloud app-centric IaC (Pulumi), or policy expressions embedded in APIs (CEL) rather than full resource graphs. ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"HCL"},"/garden/helm":{"content":"Quadrant:: Tool Ring:: #adopt URL:: https://helm.sh/ Category:: #category/devops/provisioner Related:: Kubernetes\nBlurb The package manager for Kubernetes.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Helm"},"/garden/helm-chart":{"content":"Helm Chart\nHelm 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.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Helm Chart"},"/garden/helm-unittest":{"content":"Helm unittest is a Helm plugin that unit-tests Helm Chart templates with YAML-defined suites instead of spinning up a cluster. It renders charts with helm template, then asserts on manifests (values, snapshots, document counts, and custom matchers). If you maintain charts in Kubernetes, adopt this alongside Unit Testing for application code, it catches template regressions before CI deploys.\nBlurb Unit tests for Helm charts in YAML to keep your charts consistent and robust.\nSummary Chart authors encode scenarios as test files under tests/ (or similar): set values, render templates, and compare output to expected YAML or snapshots. This is far faster and more deterministic than only testing in a live cluster, and it documents expected behavior for reviewers.\nDetails Workflow: install the plugin (helm plugin install https://github.com/helm-unittest/helm-unittest); run helm unittest in the chart directory in CI. Assertions: document presence, equals, match regex, snapshot files, and suite-level setup/teardown for values files. Fit: essential when you ship Helm Chart artifacts to customers or run many value permutations (dev/stage/prod). Limits: tests template output, not cluster behavior, pair with integration tests for admission, CRDs, and hooks. Note: superseded the earlier quintush/helm-unittest fork; use the helm-unittest org repository. ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Helm Unittest"},"/garden/hermes-agent":{"content":"Hermes is a self-improving AI Agent platform from Nous Research, personal-assistant oriented, with learning loops, messaging integrations, and Agent Skills support. We rate it hold with Moved Out: strong feature set and good fit with Ollama locally, but always-on personal agents that message external systems are inherently hard to secure; the effort to harden them often negates the convenience versus running a bounded agent in an IDE or scheduled automation you control.\nBlurb The self-improving AI agent built by Nous Research. The only agent with a built-in learning loop , it creates skills from experience, improves them during use, nudges itself to persist knowledge, and builds a deepening model of who you are across sessions.\nSummary Hermes targets the same problem space as OpenClaw, a always-available assistant across chat surfaces (Telegram, Slack, CLI, etc.) with memory, cron-style scheduling, and skill extensibility. It integrates Agent Client Protocol so editors like Obsidian can use the agent via plugins (e.g. obsidian-agent-client). On Apple Silicon, local runs via Ollama with models such as deepseek and qwen are practical.\nOriginally a credible competitor in the personal-agent wave; our position is that broad tool-and-shell access without tight policy boundaries is unsafe for most setups. Prefer IDE-bound agents (Claude Code, Cursor-class tools) or pipelines that emit reviewable scripts on a schedule, see ADR-0003-each-machine-runs-pipelines-independently for why we avoid shared always-on agent gateways across machines.\nDetails Strengths: skill learning loop, multi-channel gateway, Agent Skills portability, ACP for IDE clients, flexible model routing (cloud or Ollama). Risks: persistent credentials, messaging exfiltration, autonomous cron/subagent actions, treat as high-trust workload only with explicit sandboxing. When hold is OK: isolated experimentation, local-only models, single-user machine with clear data boundaries. When to avoid: production secrets on the same host, multi-tenant boxes, or “set and forget” automation without human review. Install: upstream documents quick install via install.sh on macOS/Linux; verify signing and update channel before production use. ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Hermes"},"/garden/hybrid-cloud":{"content":"Hybrid Cloud\nWe 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.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Hybrid Cloud"},"/garden/ide":{"content":"Under Tool, IDE covers integrated development environments and editor platforms where you write, debug, and refactor code, often with LSP, terminals, and increasingly agent panels. This is the bounded workspace we prefer over always-on messaging agents: tools stay inside your repo and session policies.\nSibling subcategories include Provisioner (configure targets) and other Tool items. Tag a product here when the primary artifact is the editor or IDE distribution (VS Code, Cursor, JetBrains, Neovim-as-IDE, Zed); not a plugin-only utility or a headless AI Agent gateway.\nGarden stance: adopt Cursor for the editor; pair with adopt cursor-agent for terminal/CI agents in the same subscription.\nAgent integration: many IDEs now host models via built-in chat or Agent Client Protocol; skills follow Agent Skills when the runtime supports them. Dev Container often pairs with VS Code–class editors for reproducible dev environments.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"IDE"},"/garden/imperative-iac":{"content":"Imperative IaC\nImperative 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 with 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 than not our first order is to use 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. Through use of proper IaC refactoring technique we arrive at code that is both clean and maintainable with minimal repetition, rendering promises of Imperative IaC moot and thus we move this to hold.","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Imperative IaC"},"/garden/inbox-pattern":{"content":"Inbox Pattern\nThe 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.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Inbox Pattern"},"/garden/incident-management":{"content":"Incident Management\nIncident Management is the practice of detecting, responding to, resolving, and learning from unplanned disruptions to production systems. It is a core discipline in SRE and DevOps, any team running a production service needs it, even informally. Without a defined process, incidents become chaotic, response time suffers, and the same problems recur.\nA mature incident management practice includes:\nDetection; alerting and monitoring that surfaces issues before users report them (see OpenTelemetry, Up-time Monitoring) Response; a defined on-call rotation, severity levels, and a clear incident commander role Communication; a status page or channel where stakeholders get updates without interrupting responders Resolution; runbooks and playbooks that responders can execute under pressure Learning; blameless postmortems that produce action items, not finger-pointing Blurb Incident management is a term describing the activities of an organization to identify, analyze, and correct hazards to prevent a future re-occurrence. These incidents within a structured organization are normally dealt with by either an incident response team (IRT), an incident management team (IMT), or the Incident Command System (ICS).\nSummary Every team shipping to production should adopt incident management, even if lightly. Start with severity definitions (P1–P3), an on-call schedule, and a postmortem template. The investment pays back immediately the first time a P1 hits and everyone knows their role. Tools like PagerDuty, Opsgenie, or even a simple Slack workflow can carry you a long way. The process matters more than the tooling.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Incident Management"},"/garden/jenkins":{"content":"Jenkins\nSecurity 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 rebuild the entire pipeline using an appropriate tool than 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 Declarative 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.","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Jenkins"},"/garden/jenkins-x":{"content":"Jenkins X\nYet 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.","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Jenkins X"},"/garden/jq":{"content":"jq is a lightweight command-line JSON processor — the default tool for slicing, filtering, mapping, and transforming JSON in shell pipelines, CI, and ad hoc debugging. We adopt it under Tool wherever JSON appears at the CLI; pair with yq when the source format is YAML (or use yq to emit JSON and pipe into jq).\nBlurb jq is like sed for JSON data — you can use it to slice and filter and map and transform structured data with the same ease that sed, awk, grep and friends let you play with text.\nSummary jq (jqlang/jq) is a portable C binary with zero runtime dependencies: filter expressions, pretty-printing, arithmetic, objects/arrays, and streaming for large inputs. The project revived under the jqlang org (1.7+ after a long hiatus); current stable is 1.8.x per jqlang.org.\nUse in one-liners (curl … | jq '.items[]'), policy checks (parse terraform show -json), and log wrangling. Prefer yq when you need YAML round-trips with comments preserved; prefer jq when JSON is the native format or you want the de facto query dialect.\nDetails Install: download (static binary), Homebrew (jq), apt/dnf packages; verify with jq --version. Docs: tutorial, manual. License: MIT (upstream). CI / ops: pipe API and tool JSON output through jq for assertions and field extraction; combine with curl, gh api, kubectl … -o json. Fit: Tool — CLI JSON query/processor. Contrast: yq for YAML-first workflows; language-native JSON APIs when a shell one-liner is not enough. ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-21T00:00:00-04:00","tags":null,"title":"jq"},"/garden/json":{"content":"JSON\nJSON is the de facto standard for data interchange on the web and in configuration files. If you are building an API, serialising config, or passing structured data between services, JSON is the default choice unless you have a specific reason to use something else (e.g. Protobuf for high-throughput binary payloads, YAML for human-authored config).\nBlurb JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language Standard ECMA-262 3rd Edition.\nSummary JSON is universally supported , every language has a parser, every HTTP client speaks it, and every developer knows it. The only reasons to consider alternatives are size/performance (Protobuf, MessagePack) or richer type systems (JSON Schema for validation). Adopt without hesitation for any API or config surface.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"JSON"},"/garden/json-schema":{"content":"JSON Schema\nJSON Schema is the standard vocabulary for describing and validating the structure of JSON documents. It is embedded in OpenAPI specs, Kubernetes CRDs, IDE autocompletion, and nearly every modern config validation pipeline. If you are exposing or consuming structured JSON at a system boundary, JSON Schema is the correct tool for documenting and enforcing that contract.\nBlurb JSON Schema is the vocabulary that enables JSON data consistency, validity, and interoperability at scale.\nSummary JSON Schema is effectively required knowledge in any DevSecOps or API-first context , OpenAPI 3.x uses it natively, Kubernetes uses it for CRD validation, and most linting/validation tools in the CI pipeline speak it. The spec is stable (Draft 2020-12) and tooling support is broad across all major languages. Adopt without reservation anywhere you need to validate or document a JSON structure.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"JSON Schema"},"/garden/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.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"K9s"},"/garden/kilo-ai":{"content":"Kilo.ai (Kilo Code) is an open-source (Apache 2.0) AI coding agent for VS Code, JetBrains, CLI, and cloud surfaces, with specialized modes (Code, Architect, Debug, Ask, Custom), multi-model support (500+ via Kilo Gateway or BYOK), MCP, and cloud agents. We rate it assess under AI Agent: a credible open-source alternative in the same lane as OpenCode and IDE-bound agents, but not yet our default stack (Cursor + cursor-agent for daily work).\nBlurb The Open Source AI Coding Agent for VS Code, JetBrains, and your CLI. Build, ship, and iterate with any model, everywhere you work.\nSummary Kilo Code (kilocode.kilo-code on VS Code / Open VSX) runs an agent loop in the editor or terminal: codebase-aware context, inline autocomplete, terminal and browser automation, code review, and human-in-the-loop controls. Kilo Auto offers a free tier without a credit card; paid tiers and BYOK cover model usage. A separate product, KiloClaw, is managed hosting for OpenClaw-style omnichannel agents (Telegram, Discord, Slack) — outside the repo-bounded AI Agent sweet spot we prefer for coding workflows.\nWorth evaluating when you want Apache-licensed, multi-surface agents with model portability and less vendor lock-in than a single IDE subscription. Compare OpenCode for terminal-first multi-provider hubs; Cursor when the editor platform is the anchor.\nDetails Install: VS Code extension kilocode.kilo-code; JetBrains plugin; CLI; also on Open VSX for VS Code–compatible editors. Docs: https://kilo.ai/docs/getting-started License: Apache 2.0 (open source). Models: 500+ via Kilo Gateway (zero markup per marketing) or bring your own API keys. Modes: Code, Architect, Debug, Ask, plus custom modes. Extras: Cloud agents, MCP, leaderboard for model rankings, KiloClaw for hosted OpenClaw. Fit: Tool / AI Agent — open-source, multi-model coding agent. Contrast: OpenCode (similar open multi-provider story); Cursor + cursor-agent (default adopt stack); Claude Code, Codex, Gemini for single-vendor CLIs. ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-21T00:00:00-04:00","tags":null,"title":"Kilo.ai"},"/garden/kube-bench":{"content":"kube-bench runs CIS Kubernetes Benchmark checks against the control plane and nodes. It reports pass/fail per hardening recommendation. We assess it alongside Kubescape before picking a standard cluster compliance scanner.\nBlurb kube-bench is a Go application that checks whether Kubernetes is deployed securely by running the checks documented in the CIS Kubernetes Benchmark.\nSummary Lens kube-bench Kubescape Focus CIS benchmark sections per K8s version Broader frameworks (NSA, MITRE, signed controls) Run model Job on node or master; JSON reports CLI, operator, CI on manifests Best fit CIS audit evidence for regulated K8s Multi-framework posture and PR checks When to use: auditors ask for CIS-aligned evidence; you need a well-known benchmark mapping per K8s minor version.\nWhen to skip: only application-level security matters; already standardized on Kubescape with overlapping CIS coverage.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"kube-bench"},"/garden/kubernetes":{"content":"Kubernetes\nBlurb Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of containerized applications.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Kubernetes"},"/garden/kubescape":{"content":"Kubescape is an open source Kubernetes security scanner. It checks clusters and manifests against NSA/CISA guidance, MITRE ATT&CK, and signed controls frameworks. We assess it next to kube-bench-style tools before standardizing a cluster compliance gate.\nBlurb Kubescape is an open-source Kubernetes security platform.\nSummary Modes: CLI scans on manifests; in-cluster operator; CI hooks for Pull Request checks on changed YAML.\nWhen to use: need a single CLI for posture reporting across many clusters; evaluating ARMO’s control catalog without buying a full CNAPP yet.\nWhen to skip: already standardized on another CNAPP or admission policy stack (Policy as Code, OPA Gatekeeper) with overlapping coverage.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Kubescape"},"/garden/kustomize":{"content":"Kustomize\nKustomize is a Kubernetes-native configuration management tool built directly into kubectl (kubectl apply -k). Rather than templating YAML like Helm, it works through overlays; you define a base set of manifests and layer environment-specific patches (dev, staging, prod) on top without modifying the originals. The result is always pure, valid YAML with no rendering step.\nWorth trialling if your team finds Helm chart authoring heavyweight or if you want to avoid a templating language entirely. That said, Helm still wins for complex deployments with external dependencies, versioned releases, and rollback support. Evaluate whether your use case fits the overlay model before committing.\nBlurb Kustomize introduces a template-free way to customize application configuration that simplifies the use of off-the-shelf applications. Now, built into kubectl as apply -k.\nSummary Kustomize shines for teams managing a small number of Kubernetes environments with predictable variance (namespace, replica count, image tag). The overlay model keeps base manifests clean and diffs readable. However, it lacks Helm’s packaging, dependency management, and release lifecycle , for anything that needs to be distributed or versioned as a unit, Helm is still the stronger choice. Trial it on a new service before adopting broadly.","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Kustomize"},"/garden/language":{"content":"Under Code, Language covers notations and domain-specific languages used to describe configuration, schemas, contracts, and structured data; not every general-purpose programming language. Examples include YAML, JSON, HCL, CEL, OpenAPI, and Protobuf. These artifacts are usually consumed by tools and runtimes rather than compiled alone like GoLang or Ruby.\nSibling subcategories: Library (reusable code modules) and Framework (opinionated application skeletons). Tag an item here when the primary artifact is the syntax or spec itself.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Language"},"/garden/library":{"content":"Under Code, Library covers reusable packages you import into an application (logging, parsing, clients, utilities) not a full runtime stack. A library gives you an API; it does not own your process lifecycle, routing, or project layout the way a Framework does.\nSibling subcategories: Language (syntax and specs like YAML) and Framework (opinionated app skeletons). Tag an item here when the primary artifact is a dependency you link or install, often per-language (e.g. Zap for GoLang, tree-sitter for parsers).","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Library"},"/garden/meercode":{"content":"Meercode is a build monitor for CI systems. It surfaces status from GitHub Actions, GitLab CI, Travis CI, Bitrise, Buddy, and similar providers in one dashboard (including desktop or wall-display views). We hold it: the product is no longer maintained. The meercode.io domain is parked, and the GitHub org has had no meaningful updates since early 2021.\nBlurb Meercode shows the status of your builds from multiple CI services in one place.\nSummary When to use: reference only if an existing Meercode deployment still runs and you are planning migration off it.\nWhen to skip: any net-new adoption. Use vendor dashboards (GitHub Actions workflow views, GitLab pipeline boards) or a small internal status board instead.\nStatus: Service appears defunct. meercode.io shows a domain parking page. The meercodeio GitHub org last updated public repos in early 2021.\nDetails Meercode launched as a SaaS build dashboard (public beta circa 2020). It aggregated CI status across several providers for team rooms and wall displays.\nTopic Notes Status Product no longer maintained; domain parked Last GitHub activity Public repos updated early 2021 Upstream github.com/meercodeio (inactive) For multi-pipeline visibility today, prefer each CI vendor’s native dashboard or a maintained open-source status board you can own.","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Meercode"},"/garden/nomad":{"content":"HashiCorp Nomad is a Platform Orchestrator that bills itself as an orchestration system that is easier than 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.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Nomad"},"/garden/octopus-deploy":{"content":"Octopus Deploy is a deployment automation platform for releases to IIS, Windows services, Kubernetes, and cloud targets. It separates build (CI) from release orchestration with tenants, channels, and runbooks. We assess it for .NET and multi-target promotion flows; GitOps or Actions-native deploy is the default elsewhere.\nBlurb Octopus Deploy is a deployment automation and release management tool that helps teams ship software to production.\nSummary When to use: Octopus is already the system of record for releases; need guided deployments with approvals and audit trails across heterogeneous targets.\nWhen to skip: container-only shops on ArgoCD; teams that want all deploy logic in git with no external release server.\nNote: Reminder title was “Octopus CD”; product name is Octopus Deploy.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Octopus Deploy"},"/garden/ollama":{"content":"Ollama is a local runtime for pulling, serving, and chatting with open-weight LLMs on your machine (macOS, Linux, Windows). We rate it trial: the default way to run models locally for development, AI Agent backends, and privacy-sensitive experiments, pair it with agents like Hermes or any OpenAI-compatible client, but expect throughput limits vs cloud APIs for heavy automation.\nAI models (even when optimized) are very GPU intensive so do not expect to be able to do other things when the agent loop is running. This limits the usefullness to only when privacy is the only concern.\nBlurb Get up and running with large language models locally.\nSummary Ollama packages model weights, GPU/Metal acceleration, and a simple CLI (ollama run, ollama pull) behind a local HTTP API. Most integrations use its OpenAI-compatible endpoints so tools can swap base_url to http://localhost:11434 without bespoke SDKs, aligned with our OpenAI-compat provider approach for pipelines.\nStrong fit on Apple Silicon and Linux workstations for iterative prompt work, offline use, and agent loops that should not send data to a hosted model. Less ideal as the sole backend for high-volume batch jobs unless you size hardware and accept latency.\nDetails Workflow: install Ollama → ollama pull <model> → run server (often automatic) → point clients/agents at local API. Models: catalog includes Llama, Mistral, Qwen, DeepSeek, and others; tags vary by hardware (e.g. Hermes notes deepseek and qwen on Apple Silicon). Integrations: Hermes and other AI Agent platforms; any OpenAI-shaped HTTP client. Limits: single-machine scale, model RAM/VRAM caps, no managed HA. Use cloud providers when you need SLA, burst capacity, or centralized billing. Security: keeps prompts on-box; still vet models and plugins; not a substitute for enterprise model governance. ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Ollama"},"/garden/openapi":{"content":"OpenAPI is a specification and tool set to make designing and documenting APIs easier. If you are designing a REST API then this is the way to go. However, there are other popular API alternatives like gRPC, and GraphQL.","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"OpenAPI"},"/garden/openclaw":{"content":"OpenClaw is an open-source, self-hosted personal AI Agent that runs continuously on your hardware and connects to messaging apps (WhatsApp, Telegram, Slack, Discord, and others) with skills, shell access, and persistent memory. We rate it hold: powerful on paper, but our experience matches Hermes, too complex and fragile for multi-machine, sandboxed setups; prefer bounded IDE agents or per-machine scheduled pipelines.\nBlurb OpenClaw is your personal AI assistant that runs on your own hardware.\nSummary OpenClaw targets always-on assistance: ingest messages from chat surfaces, call cloud or local models (Ollama via OpenRouter-style routing), execute skills/plugins, and remember context in local files. The GitHub project (openclaw/openclaw) gained massive attention for automating real workflows; not just chat.\nDetails Strengths: broad channel support, skill ecosystem, self-hosted narrative, multi-model flexibility. Risks: broad system/shell access, credential sprawl across chat networks, hard to secure; operational burden rivals the task. vs Hermes: same product category; Hermes adds learning loops and ACP; both are hold for greenfield personal automation here. Preferred pattern: deterministic scripts + Dagu (or cron) on each machine; LLM only where reasoning is required; agents inside IDE when interactive help is needed. ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"OpenClaw"},"/garden/opencode":{"content":"OpenCode is an open-source AI coding agent (terminal TUI, desktop app, and IDE extension) that connects to 75+ LLM providers (Claude, GPT, Gemini, local models, GitHub Copilot, ChatGPT accounts) with LSP-aware context and multi-session parallelism. We rate it trial under AI Agent: strong when you want vendor-neutral agents and privacy-sensitive workflows; we usually default to Cursor + cursor-agent for daily editor work and vendor-specific CLIs (Claude Code, Codex, Gemini) when billing is already tied to one stack.\nBlurb The open source AI coding agent. Free models included or connect any model from any provider, including Claude, GPT, Gemini and more.\nSummary OpenCode (anomalyco/opencode) runs a plan/build agent loop in your repo: /init generates AGENTS.md, sessions support share links and undo/redo, and MCP extends tool use. OpenCode Zen offers curated models benchmarked for coding agents. The project does not store your code or context on OpenCode servers, suited to privacy-sensitive environments per their docs.\nUse for interactive coding when model choice and open-source auditability matter more than a single-vendor CLI. It is a repo-bounded AI Agent; not an omnichannel personal bot (OpenClaw, Hermes are hold). For scheduled automation, prefer deterministic scripts and Dagu (which can invoke opencode as a harness) with LLM steps only where reasoning is required.\nCompare Claude Code, Codex, and Gemini for single-vendor agent loops; OpenCode is the multi-provider hub.\nDetails Install: curl -fsSL https://opencode.ai/install | bash, npm install -g opencode-ai, Homebrew (anomalyco/tap/opencode), Docker image ghcr.io/anomalyco/opencode. Auth: Provider API keys via /connect, OpenCode Zen billing, or reuse ChatGPT Plus / GitHub Copilot logins where supported. Models: Any provider in the Models.dev directory; Zen subset for validated coding-agent performance. Surfaces: terminal (primary), desktop beta (macOS/Windows/Linux), IDE extension. Fit: Tool / AI Agent, open, multi-model coding agent. Contrast: Cursor + cursor-agent for default IDE workflow; vendor CLIs when you stay in one ecosystem. ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"OpenCode"},"/garden/opentelemetry":{"content":"OpenTelemetry (OTel) is the CNCF-standard way to collect traces, metrics, and logs with vendor-neutral APIs and SDKs. Instrument once, export to your backend of choice (Prometheus, Jaeger, vendor SaaS, etc.) instead of locking into a proprietary agent. For new services and platforms we treat OTel as the default observability foundation, it is what makes Incident Management detection and Up-time Monitoring actually work in modern, ephemeral infrastructure.\nBlurb High-quality, ubiquitous, and portable telemetry to enable effective observability.\nSummary OTel defines semantic conventions, context propagation, and collectors so telemetry is consistent across languages and runtimes. Most teams start with auto-instrumentation or lightweight SDK hooks, run the OpenTelemetry Collector for routing and redaction, then wire exporters to existing analysis tools.\nDetails Signals: traces (distributed requests), metrics (aggregates), logs (events), often correlated via shared trace context. Neutrality: avoids the “three proprietary agents” problem; switching vendors should not require re-instrumenting applications. Collector: optional pipeline for batching, sampling, PII scrubbing, and fan-out to multiple backends. Maturity: widely supported in Kubernetes workloads, GoLang, Java, .NET, Python, and major cloud APM products. ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"OpenTelemetry"},"/garden/orchestrator":{"content":"An orchestrator handles the scheduling and monitoring of processes on a host or across hosts.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Orchestrator"},"/garden/planning-poker":{"content":"Planning Poker\nScrum 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, everyone 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.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Planning Poker"},"/garden/platform":{"content":"A platform is something you build on top of. For example: Android or Cloud","garden":{"kind":"category"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Platform"},"/garden/policy-as-code":{"content":"Policy as Code\nPolicy as Code is the practice of expressing compliance, security, and operational rules as version-controlled, machine-executable code , enforced automatically in the pipeline rather than reviewed manually after the fact. It is a direct application of Shift Left thinking to governance: if a policy can be checked by a machine, it should be, and it should fire before anything reaches production.\nPolicies cover a wide surface: Kubernetes admission control, IaC compliance (Terraform, Helm chart rules), API authorization, network firewall rules, and DevSecOps audit requirements. The leading engine is Open Policy Agent (OPA), which uses the Rego language and integrates across the stack via Conftest (for config files) and Regula (for IaC).\nBlurb OPA is an open source, general-purpose policy engine that unifies policy enforcement across the stack. OPA provides a high-level declarative language (Rego) that lets you specify policy as code and exposes simple APIs to offload policy decision-making from your software.\nSummary Policy as Code is a strong adopt in any DevSecOps or regulated environment. Manual compliance reviews don’t scale and introduce human error; codified policies run on every PR, in every environment, consistently. Start with the highest-risk surface (Kubernetes admission, IaC security rules) and expand from there. OPA + Conftest is the recommended starting point , the learning curve for Rego is real but the payoff in automated governance is significant. Pairs well with GitOps: policy checks become just another gate in the pull request pipeline.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Policy as Code"},"/garden/protobuf":{"content":"Protobuf\nProtocol Buffers (Protobuf) is Google’s language-neutral binary serialization format. You define message schemas in .proto files, and the compiler generates strongly-typed serialization code in your language of choice. The binary encoding is significantly smaller and faster to parse than JSON, making it the default wire format for gRPC-based services.\nThe tradeoff is observability: binary payloads aren’t human-readable without tooling, which complicates debugging and log inspection. For this reason Protobuf is best suited to high-throughput internal service-to-service communication where performance matters , not for public-facing APIs or anywhere humans need to read the wire format directly.\nBlurb Protocol Buffers are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data , think XML, but smaller, faster, and simpler.\nSummary Trial Protobuf when you have a performance bottleneck on an internal API boundary and are already using or considering gRPC. The schema-first approach enforces strong contracts between services and code generation eliminates serialization bugs. However, the operational overhead (proto file management, generated code, binary debugging) is real , don’t reach for it when JSON + REST is sufficient. Pairs naturally with gRPC; less useful without it.","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Protobuf"},"/garden/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 tools 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.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Pulumi"},"/garden/python":{"content":"Python is a general-purpose language with dominant libraries in automation, data, and ML. We rate it trial: expect to read and maintain Python when your stack already depends on it (Ansible, notebooks, data pipelines), but prefer faster or stricter languages for latency-sensitive services unless the team standardizes on Python end-to-end.\nBlurb Python is a programming language that lets you work quickly and integrate systems more effectively.\nSummary Python grew out of academia and scripting culture: batteries-included stdlib, readable syntax, and a huge PyPI ecosystem made it the default glue for DevOps (Ansible controllers), scientific computing (NumPy, pandas), and ML tooling. That ubiquity is both strength and trap, teams often reach for Python because a library exists, then inherit GIL limits, packaging drift, and runtime cost at scale.\nTreat Python as a toolchain language: adopt it where the framework or community is Python-first; for new backend services, compare GoLang (ops and concurrency), Ruby (legacy web), or typed stacks before defaulting to Python for convenience alone.\nDetails Where it wins: config management controllers, data/ML notebooks, quick automation, teaching and prototypes, AWS CDK / Pulumi when you already committed to imperative IaC (see CDKs and garden notes on Imperative IaC). Where it hurts: CPU-bound APIs, tight tail latency, and large multi-process deployments without careful architecture (async, workers, native extensions). Operations: virtualenv/uv/poetry discipline matters; pin dependencies in CI; avoid sprawling monoliths without type hints or tests. Pair with: Ansible (adopt tool, Python on control node); contrast GoLang for infra CLIs and long-running services. ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Python"},"/garden/replicated":{"content":"Quadrant:: Platform Ring:: #hold URL:: https://www.replicated.com/ Related:: Kubernetes\nBlurb Distribute Apps to Customers as K8s.\nSummary Provides a license service and proxy in front of the OSS items kURL, and KOTs. However, those OSS are still pretty immature often leading to breaking changes and the Replicated commercial costs does not reduce these issues or reduce effort on their customers.\nBest to review kURL and KOTs vs Rancher and determine what your direct customers would be able handle, and implement your own solution on top.\nMost customers are not going to be able to take Replicated and manage it as well the underlying product. So in the end it does more harm then good.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Replicated"},"/garden/rest":{"content":"Representational State Transfer (REST)\nBlurb REST defines a set of constraints for how the architecture of a distributed, Internet-scale hypermedia system, such as the Web, should behave","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Representational State Transfer (REST)"},"/garden/ruby":{"content":"Ruby is a dynamic, expressive language optimized for developer ergonomics and metaprogramming. We rate it hold for new work: it remains viable for maintaining existing codebases (especially Ruby on Rails), but hiring depth, runtime performance, and ecosystem momentum outside that niche favor other stacks for greenfield systems.\nBlurb Ruby is a language of careful balance. Its creator, Yukihiro “Matz” Matsumoto, blended parts of his favorite languages (Perl, Smalltalk, Eiffel, Ada, and Lisp) to form a new language that balanced functional programming with imperative programming.\nSummary In the 2000s Ruby stood out for treating the programmer as the customer, readable syntax, blocks, and open classes made it feel like a DSL you could shape in place. That flexibility powered the Rails era but also encouraged “magic” (monkey patches, implicit requires, metaprogramming) that is hard to audit in large teams.\nOutside the Rails corridor, Ruby sees less investment in performance (GIL, interpreter overhead) and tooling compared to GoLang for infra or Python for data/ML. Keep Ruby when you inherit a mature app or a team that already ships it; do not default to it for new platforms.\nDetails Sweet spot: long-lived web monoliths on Ruby on Rails; scripting and glue where team expertise already exists. Friction: slow cold starts and CPU-bound workloads vs compiled or JIT languages; global interpreter lock limits naive threading models. Ecosystem: gems are plentiful but quality varies; operations patterns often trace to Capistrano-era deploys rather than modern container-native defaults. Pair with: Ruby on Rails (framework, also hold for greenfield); prefer Python or GoLang when starting net-new services without a Ruby mandate. ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Ruby"},"/garden/ruby-on-rails":{"content":"Ruby on Rails is a full-stack web framework built on Ruby that popularized convention-over-configuration, ActiveRecord as an ORM, and MVC for server-rendered apps. It made CRUD and early SaaS prototypes fast in the 2000s, but we rate it hold for new work: scaling and team velocity usually require peeling off the framework’s opinions long before the product is mature.\nBlurb Rails is a web-application framework that includes everything needed to create database-backed web applications according to the Model-View-Controller (MVC) pattern.\nSummary Rails proved that productive frameworks could ship real products quickly, scaffolding, migrations, and a cohesive standard library lowered the bar for web startups. That same cohesion becomes a liability when you need independent scaling of read/write paths, strict performance budgets, or polyglot services. Teams often discover too late that the fastest path forward is a partial strangler off Rails rather than incremental tuning inside it.\nDetails Strengths (historical): rapid prototyping, strong conventions, batteries-included auth/jobs/mailers, large gem ecosystem. Weaknesses (today): global state and “magic” complicate reasoning; ActiveRecord encourages fat models; multi-process scaling and background work add operational cost compared to lighter stacks. Operations: legacy deployments often paired Rails with Capistrano; local dev tools like Pow addressed Rack host naming before modern DNS helpers. When hold is OK: maintaining an existing Rails monolith with a committed team; greenfield work should default to stacks that match your expected scale and hiring pool (often not Ruby). ","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Ruby on Rails"},"/garden/sarif":{"content":"SARIF (Static Analysis Results Interchange Format) is an OASIS JSON schema for exchanging output from Code Scanner tools in CI and IDEs. We adopt it as the default interchange format when wiring SAST, IaC scanners, and GitHub Actions annotations on Pull Requests.\nBlurb SARIF defines a JSON-based format for the output of static analysis tools.\nSummary Why it matters: one schema lets CodeQL, Semgrep, Conftest, and vendor scanners feed the same PR comment bots, dashboards, and policy gates.\nWhen to use: any pipeline that aggregates multiple scanners; GitHub Advanced Security code scanning; internal security portals that ingest findings.\nWhen to skip: a single proprietary scanner with no export and no plan to unify findings (still prefer SARIF export if available).\nPairs with: sarif artifacts uploaded as workflow artifacts; GitHub upload-sarif action; Policy as Code rules that count severities per run.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"SARIF"},"/garden/scrum":{"content":"Scrum is one of the more popular Agile Software Development techniques. Interestingly enough it is just Waterfall 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 parts 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.","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Scrum"},"/garden/shell-operator":{"content":"Shell Operator is a Kubernetes operator framework that runs shell or bash hooks when cluster objects change. It watches CRDs or built-in resources and executes scripts you package in the image. We assess it for quick glue automation before committing to a full Go operator.\nBlurb Shell-operator is a tool for running event-driven scripts in a Kubernetes cluster.\nSummary When to use: small integrations (notify, label, trigger jobs) where a compiled operator is overkill; teams comfortable maintaining hook scripts and images.\nWhen to skip: complex reconciliation loops, admission webhooks, or production controllers that need strong typing and tests in Go.\nOps note: treat hook scripts like production code: pin images, limit RBAC, and log hook failures to OpenTelemetry or cluster logging.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Shell Operator"},"/garden/shift-left":{"content":"Quadrant:: Technique Ring:: #adopt URL:: Category:: Related:: Terraform, Pulumi\nBlurb Shift left is the practice of moving testing, quality, and performance evaluation early in the development process, often before any code is written\nSummary The idea is that if you can write it as code then you can move testing to it and things move from Pets to Cattle.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Shift Left"},"/garden/shiftleft":{"content":"Quadrant:: Technique Ring:: #adopt URL:: Category:: Related:: Terraform, Pulumi\nBlurb Shift left is the practice of moving testing, quality, and performance evaluation early in the development process, often before any code is written\nSummary The idea is that if you can write it as code then you can move testing to it and things move from Pets to Cattle.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-17T00:00:00-04:00","tags":null,"title":"Shift Left"},"/garden/single-responsibility-principle":{"content":"Single Responsibility Principle\nThe Single Responsibility Principle (SRP) is the S in SOLID. A module, class, or function should have one (and only one) reason to change. In practice this means each unit of code owns a single concern: a data transformer shouldn’t also handle I/O, a config parser shouldn’t also validate business rules.\nSRP is not about making things small for its own sake. It is about coupling: when a piece of code changes for two different reasons, those two reasons will eventually conflict, and every change risks breaking the other concern. Keeping responsibilities separate keeps change surfaces small and test boundaries clean.\nBlurb A class should have one, and only one, reason to change. , Robert C. Martin\nSummary SRP is foundational to maintainable, testable code and applies at every level of granularity (functions, classes, services, and microservices. Violations are usually visible as functions with “and” in their name, classes with unrelated method clusters, or modules that import from two completely different domains. The fix is almost always to extract: pull the second concern into its own unit with a clear interface. Adopt unconditionally) this principle pays dividends at every scale.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Single Responsibility Principle"},"/garden/single-sign-on":{"content":"Single Sign-on\nSingle sign-on (SSO) lets a user authenticate once with an identity provider (IdP) and access multiple applications without separate passwords per app. We adopt SSO for B2B Software as a Service and internal tools: it is Enterprise Ready table stakes, pairs with RBAC, and reduces password sprawl when implemented with standard protocols.\nBlurb Single sign-on (SSO) is an authentication scheme that allows a user to log in with a single ID to any of several related, yet independent, software systems.\nSummary Protocols (prefer standards):\nProtocol Typical use OIDC Modern SaaS, SPAs, mobile; built on OAuth 2.0 SAML 2.0 Enterprise IdP connections (Okta, Entra ID, Google Workspace) OAuth 2.0 Delegated API access (not full SSO by itself) When adopt applies:\nSelling to enterprises that mandate their IdP (Enterprise Ready) Workforce apps (dashboards, ArgoCD, Boundary (Hashicorp)) via corporate OIDC Customer orgs with “login with your company’s SSO” on your product Implementation paths:\nApproach Garden notes Embedded IdP Auth0 / FrontEgg (assess) for product login + SAML/OIDC connections Cloud-native Cognito, Entra, Google Identity for simpler estates Self-hosted Keycloak-class when residency or control demands it Security expectations: MFA at the IdP, short session lifetimes, audit logs, and Access on Demand for privileged production access (SSO is not the same as standing admin).\nDetails Topic Notes SP vs IdP Your app is the service provider; customer brings the IdP metadata JIT provisioning Map SAML/OIDC claims to users and RBAC roles SCIM Optional next step for directory sync (not required for basic SSO) Testing Staging IdP connection per customer; document clock skew and cert expiry Break-glass Local emergency accounts outside SSO, vaulted and audited Garden pattern: adopt SSO on every customer-facing SaaS before enterprise sales; assess which IdP product fits. Do not build custom password stores for B2B if SSO is on the roadmap.\nReferences\nWikipedia: Single sign-on Enterprise Ready ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Single Sign-on"},"/garden/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.","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Skaffold"},"/garden/slack":{"content":"Slack is a team messaging and collaboration platform (channels, DMs, search, apps). We rate it trial: fine as the default human coordination layer when the org already pays for it; do not let Slack become your control plane for production.\nBlurb Slack is your productivity platform.\nSummary Use Slack for: team chat, incident coordination rooms, lightweight notifications, and linking out to real systems (tickets, dashboards, runbooks).\nAvoid Slack for: ChatOps-style bot commands against prod, mixing pager-grade alerts with casual channels, or granting integrations broad cloud/API scopes because “it’s convenient.”\nGarden alignment: ChatOps is hold; Slack’s app marketplace encourages patterns we reject for DevSecOps. Prefer dedicated on-call (Incident Management) and audited automation outside chat.\nDetails Topic Notes Alerting Mirror critical alerts into a dedicated channel; do not rely on Slack alone for paging Integrations Review OAuth scopes and retention; treat bots like production software Agents Omnichannel agents (OpenClaw, hermes-agent) can attach here; same hold caution applies Alternatives Microsoft Teams, Google Chat, or matrix-style tools when customers mandate them ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-21T00:00:00-04:00","tags":null,"title":"Slack"},"/garden/slas":{"content":"SLAs\nService level agreements (SLAs) are measurable commitments between a provider and a customer: availability, response time, support windows, and remedies when targets are missed. We adopt defining SLAs (and the SLO/SLI machinery behind them) for Software as a Service and managed platforms so sales, engineering, and ops share the same numbers.\nBlurb A service-level agreement (SLA) defines the level of service expected by a customer from a supplier, laying out the metrics by which that service is measured and the remedies or penalties, if any, should the agreed-on service levels not be achieved.\nSummary SLA vs SLO vs SLI:\nTerm Meaning SLI What you measure (e.g. successful requests / total) SLO Internal target (e.g. 99.9% over 30 days) SLA Customer-facing promise, often with credits or termination rights Why adopt:\nEnterprise Ready procurement expects written uptime and support tiers Aligns product and DevOps on error budgets before Continuous Deployment pace outruns reliability Forces honest capacity planning (see SRE practices) Common SLA metrics:\nMetric Example commitment Availability 99.9% monthly API uptime Latency p95 < 300ms for core APIs Support P1 response within 1 hour (business hours) RTO/RPO Recovery objectives for disaster scenarios What to avoid: marketing “five nines” without instrumentation; SLAs you cannot measure from production telemetry; unlimited liability without error-budget policy.\nDetails Topic Notes Measurement SLIs from real probes and RED metrics; no manual spreadsheet honesty Exclusions Document maintenance windows, customer-caused outages, force majeure Credits Tie remedies to repeatable formulas; legal review Internal SLO Set SLO stricter than SLA so you miss the customer line rarely Incidents Postmortems when SLO burn rate spikes; feed back to architecture Garden pattern: adopt published SLAs (or clear “best effort” docs) for any paid multi-tenant product; pair with on-call, status pages, and deployment policies that respect error budgets.\nReferences\nWikipedia: Service-level agreement Google SRE: SLI, SLO, SLA ","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"SLAs"},"/garden/software-as-a-service":{"content":"Software as a Service\nSoftware as a Service (SaaS) means the vendor runs the software; customers use it over the network on a subscription or consumption model. We rate the delivery model assess: powerful for product velocity and ops efficiency, but not every problem should be SaaS, security boundary, tenancy, compliance, and customer data residency must drive the decision.\nBlurb Software as a service is a software licensing and delivery model in which software is licensed on a subscription basis and is centrally hosted.\nSummary When SaaS fits: multi-tenant products, frequent releases, Continuous Deployment, per-tenant Feature Flags, and teams that can live by 12 Factor App / Cattle Not Pets assumptions (stateless app tiers, managed data stores).\nWhen to hesitate: regulated data on shared infrastructure, air-gapped customers, heavy customization per tenant, or “SaaS” as an excuse to skip DevSecOps (shared responsibility is still yours).\nB2B SaaS stack: identity (Auth0, FrontEgg), observability (OpenTelemetry), and GitOps delivery (ArgoCD) are common companions; each has its own garden rating.\nDetails Topic Notes Tenancy Single-tenant vs multi-tenant affects cost, isolation, and compliance story Security You own app-layer controls; vendor owns platform; map both in contracts and architecture Delivery Prefer Continuous Deployment over manual releases when uptime SLAs allow Naming Also referenced as SaaS throughout the garden ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Software as a Service"},"/garden/spinnaker":{"content":"Spinnaker is an open source multi-cloud continuous delivery platform. It models pipelines, stages, and deployments (including canaries) across AWS, GCP, Kubernetes, and more. We assess it for brownfield Netflix-style CD; greenfield GitOps with ArgoCD is usually simpler to operate.\nBlurb Spinnaker is an open source, multi-cloud continuous delivery platform for releasing software changes with high velocity and confidence.\nSummary Strengths: mature deployment strategies (red/black, canary); broad provider support; strong fit when teams already invested in Spinnaker ops.\nWeaknesses: heavy operational footprint (multiple microservices); steep learning curve; many orgs now prefer Git-declared desired state plus ArgoCD or managed CD.\nWhen to use: existing Spinnaker estate; complex multi-account AWS/GCP promotion with battle-tested runbooks.\nWhen to skip: new platforms; small teams; Kubernetes-only shops that can standardize on Argo CD or GitHub Actions deploy workflows.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Spinnaker"},"/garden/sre":{"content":"SRE\nSite 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/ ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"SRE"},"/garden/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.","garden":{"kind":"item","usefulness":"hold"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"System Initiative"},"/garden/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 solution. However, in this day and age you are likely better served with a ZTNA solution, so we remain cautious.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Tailscale"},"/garden/tailwind-css":{"content":"Tailwind CSS is a utility-first CSS framework. You compose layouts from small classes in markup instead of hand-writing large stylesheets. We assess it for internal web UIs and marketing sites; backend-heavy work does not require it.\nBlurb Tailwind CSS is a utility-first CSS framework for rapidly building custom user interfaces.\nSummary When to use: React/Vue/Next sites where designers want fast iteration; teams comfortable with class-heavy HTML and a build step (PostCSS).\nWhen to skip: no customer-facing CSS; design system already locked on another stack; email templates and PDFs that cannot use utility pipelines.\nPairs with: component libraries (Headless UI, shadcn); design tokens via tailwind.config; purge/content paths in CI for small bundles.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Tailwind CSS"},"/garden/technique":{"content":"A technique that is used during implementation. It could be a design pattern, specification, or similar.","garden":{"kind":"category"},"lastmodified":"2026-05-16T00:00:00-04:00","tags":null,"title":"Technique"},"/garden/terraform":{"content":"Quadrant:: Tool Ring:: #adopt URL:: https://www.terraform.io/ Category:: Related:: Pulumi\nBlurb Automate infrastructure on any cloud with Terraform.\nSummary This is the de facto DevOps tool for managing infrastructure. It does so via IaC files\nDetails What else is important to know?","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Terraform"},"/garden/test-framework":{"content":"Under Code, Test Framework covers libraries and tools whose primary job is to author, organize, and run automated tests, runners, assertion libraries, mocks, and chart/template testers. This is not Unit Testing or Integration Testing themselves (those are Technique notes); it is the concrete software you pick to implement them.\nSibling subcategories: Language, Library, and Framework (application stacks). Tag an item here when the product exists mainly to support test execution (e.g. Helm Unittest for Helm Chart templates, TestContainer for integration dependencies) not when testing is a side feature of a larger platform.","garden":{"kind":"subcategory"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Test Framework"},"/garden/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.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Test Pyramid"},"/garden/testcontainers":{"content":"Testcontainers is a family of libraries that start real service dependencies (databases, brokers, etc.) in Docker containers during automated tests, then tear them down. Docker acquired AtomicJar (the company behind Testcontainers) in 2023; the project remains open source with modules for Java, Go, Python, .NET, and others. We rate it assess: powerful for integration tests that need fidelity beyond mocks, but it adds Docker-in-CI requirements and runtime cost compared to Unit Testing alone.\nBlurb Unit tests with real dependencies.\nSummary Tests declare dependencies in code (for example, a PostgreSQL or Redis container), Testcontainers pulls images, waits for readiness, exposes connection URLs to the test, and cleans up via its Ryuk sidecar. This replaces hand-rolled docker run scripts and reduces flakiness from shared test environments.\nDetails vs Container Structure Test: Container Structure Test validates image layout; Testcontainers runs services for application tests, complementary, not interchangeable. Strengths: realistic integration coverage, repeatable local and CI runs, large module catalog (Postgres, Kafka, LocalStack, etc.). Requirements: a container runtime on the host/CI agent (Docker or compatible); plan image caching and parallelism to keep pipelines fast. Trade-offs: slower than pure unit tests; not a substitute for contract tests or production-like staging environments. When to trial: microservices with external state, data access layers, or messaging, especially when mocks have drifted from production behavior. ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Testcontainers"},"/garden/tool":{"content":"A tool is something that helps during the development process. It could be something like an IDE, or a provisioner.","garden":{"kind":"category"},"lastmodified":"2026-05-16T00:00:00-04:00","tags":null,"title":"Tools"},"/garden/travis-ci":{"content":"Travis CI is a hosted continuous integration service tied closely to GitHub (historically the default for OSS .travis.yml builds). We assess it for legacy repos only. New pipelines should land on GitHub Actions unless a contract or org mandate says otherwise.\nBlurb Travis CI is a hosted continuous integration service used to build and test software projects hosted on GitHub and other providers.\nSummary Role: Continuous Integration on push and pull request via .travis.yml; optional deploy stages for Continuous Delivery to cloud or static hosts.\nWhen to use: an existing Travis pipeline still runs and migration cost exceeds near-term value; enterprise Travis.com matches compliance or billing requirements you cannot meet on Actions.\nWhen to skip: new repositories; need OIDC to cloud deploys, reusable workflows, org-wide templates, or monorepo path filters (GitHub Actions and GitLab CI cover this better).\nNot the same as: GitHub Actions (native GitHub workflows); Jenkins (self-hosted pet server, hold). Travis is hosted SaaS with YAML in-repo.\nDetails Topic Notes Config .travis.yml at repo root; language matrix, script, before_install, optional deploy History Travis CI Org (travis-ci.org) ended free public builds; Travis.com is the supported product Pricing Paid usage-based tiers only since 2021; no standing free tier for open source Secrets Encrypted variables in Travis settings; no first-class OIDC to AWS/GCP/Azure like Actions Runners Hosted Linux/macOS/Windows workers; concurrency limits depend on plan Integrations GitHub App or OAuth; Bitbucket and Perforce on enterprise Server tier Migration off Travis: use GitHub Actions Importer for a dry-run, or hand-port matrix jobs to .github/workflows/*.yml. Remove .travis.yml and rotate any secrets that lived only in Travis.\nPractices for legacy repos: pin base images; avoid long-lived cloud keys in Travis vars; set a migration deadline when credits or contract terms end.\nReferences\nTravis CI documentation Migrating from Travis CI to GitHub Actions ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-27T00:00:00-04:00","tags":null,"title":"Travis CI"},"/garden/tree-sitter":{"content":"Tree-sitter is an incremental parser generator and runtime that produces concrete syntax trees and updates them efficiently as source changes. We rate it assess: the default foundation when you are building editor features, multi-language tooling, or analyzers, but most product teams should consume it indirectly (via an editor or analyzer) rather than embed parsers themselves.\nBlurb Tree-sitter is a parser generator tool and an incremental parsing library. It can build a concrete syntax tree for a source file and efficiently update the syntax tree as the source file is edited.\nSummary Unlike batch parsers that assume valid input, tree-sitter is designed for interactive use: partial parses stay useful, grammars are maintained per language, and the same core powers syntax highlighting in Neovim, Helix, GitHub, and Zed. You get fast, error-tolerant CSTs suitable for highlighting, structural search, and simple transforms.\nWhen requirements grow into cross-node semantic graphs (data flow, control flow, security models), add tree-sitter-graph on top. For one language and a stable compiler front-end, a native parser or generator like ANTLR may be simpler than adopting the tree-sitter ecosystem.\nDetails Strengths: incremental updates, robust parsing of incomplete code, broad grammar catalog, bindings for Rust/C/Node/Wasm/Python/Go and more. Workflow: define or reuse a grammar → generate parser → run queries (pattern match on syntax nodes) in your tool. Typical uses: editor syntax highlighting, code outline/folding, lightweight lint rules, test runners that need structure-aware selection. Heavier uses: static analysis pipelines, often paired with tree-sitter-graph (CodeQL-style stacks). Category: Code / Library, a dependency you link, not a standalone service. Pair with: tree-sitter-graph when CST queries are not enough; contrast language-specific compiler APIs when you only target one language. ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"tree-sitter"},"/garden/tree-sitter-graph":{"content":"tree-sitter-graph is a Rust library and CLI that layers a graph DSL on top of tree-sitter parse trees, turning syntax nodes into arbitrary graphs with nodes, edges, and attributes. We rate it assess: essential when you are building static-analysis or IDE pipelines that need custom semantic graphs (GitHub uses it in the CodeQL stack), but overkill if you only need parsing or simple AST walks.\nBlurb The tree-sitter-graph library defines a DSL for constructing arbitrary graph structures from source code that has been parsed using tree-sitter.\nSummary tree-sitter gives you fast, incremental concrete syntax trees; tree-sitter-graph adds stanzas, tree-sitter query patterns plus statements that emit graph nodes, link edges, and attach attributes. That separation lets tools map many languages through shared grammar work while encoding language-specific semantics in the graph rules (control flow, data flow, name resolution, etc.).\nTypical consumers are security analyzers, linters with cross-file models, and language tooling that outgrows ad hoc AST visitors. You need comfort with tree-sitter queries and the graph DSL before committing; most application teams should depend on a finished analyzer rather than embed this directly.\nDetails Depends on: tree-sitter parsers and grammars for each language you target. Delivery: Rust crate with optional CLI (cargo install --features cli tree-sitter-graph); graphs can serialize to JSON for downstream tools. Mental model: syntax nodes (from the parse) vs graph nodes (your derived model); stanzas bridge the two. When to skip: one-off scripts, simple formatters, or projects where tree-sitter alone plus hand-written visitors is enough. Pair with: tree-sitter (parser foundation); lives under Library in the Code category. ","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"tree-sitter-graph"},"/garden/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.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Wireguard"},"/garden/work-queue":{"content":"Work queues decouple producers and consumers so tasks can be processed asynchronously, retried, and scaled independently. They pair well with the Inbox Pattern when you need at-least-once delivery without building a full workflow engine. Adopt a mature broker (or managed queue) before inventing your own persistence layer.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Work Queues"},"/garden/workflow":{"content":"Workflow is the orchestration of multi-step business or technical processes, often with state, retries, timeouts, and human approvals. In software it maps to engines such as Dagu or to staged CI-CD Tools pipelines. Model workflows explicitly when coordination across services or teams is the bottleneck.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Workflow"},"/garden/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.","garden":{"kind":"item","usefulness":"adopt"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"YAML"},"/garden/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.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"YAMLScript"},"/garden/zanzibar":{"content":"Zanzibar\nGoogle 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 you 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 correct 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 use 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 reduce all permissions to their simplest 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/","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Zanzibar"},"/garden/zap":{"content":"Zap (Uber) is a structured, leveled logging library for GoLang; not to be confused with the Zed Attack Proxy (Zap) security scanner. We rate it trial for new Go services: it remains a high-performance default when you want JSON logs and explicit fields, though Go 1.21+ log/slog in the standard library is now the first choice to evaluate before adding a third-party logger.\nBlurb Blazing fast, structured, leveled logging in Go.\nSummary Zap optimizes for low allocation and clear leveled output (Debug, Info, Warn, Error) with strongly typed fields (zap.String, zap.Int, …) instead of printf-style formatting. Production configs favor JSON encoders; development configs often use console encoding with color. The API splits a terse zap.NewProduction() path from a configurable zap.Config for teams that need tuning.\nIf you are greenfield on modern Go, prototype with log/slog first, stdlib support, handler ecosystem, and attr-style fields cover many of the same goals. Reach for Zap when benchmarks, existing Uber-style instrumentation, or team standards already assume it.\nDetails Category: Code / Library, imported into your binary, not a standalone agent. Strengths: speed, structured fields, sane defaults for production JSON logging, mature usage across the Go ecosystem. Trade-offs: another dependency to version; learning curve vs slog; avoid mixing multiple logging facades in one service. Operations: pair with centralized log aggregation (JSON lines → Loki/ELK/CloudWatch); define level and sampling policy per environment. Disambiguation: OWASP Zed Attack Proxy (Zap) is unrelated tooling for DAST. ","garden":{"kind":"item","usefulness":"trial"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"Zap"},"/garden/ztna":{"content":"Zero Trust Network Access (ZTNA) grants application-level access based on identity and policy instead of traditional perimeter VPNs. Users reach specific services through a broker after device and posture checks. Prefer ZTNA over broad VPN access for remote and third-party users; use network VPNs such as Tailscale only when you truly need layer-3 connectivity.","garden":{"kind":"item","usefulness":"assess"},"lastmodified":"2026-05-18T00:00:00-04:00","tags":null,"title":"ZTNA"},"/linkindex/index.json":{"content":"","lastmodified":"0001-01-01T00:00:00Z","tags":null,"title":"Link Index"},"/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 20 21 #!/usr/bin/env bash # Unset the PAGER to prevent interactive behavior unset PAGER # Update main to remote, and branches that were deleted in the remote # NOTE: \"-d\" will only delete if everything is merged, which doesn't work for # squash commits. so use \"-D\" instead git fetch -p \\ && git checkout main \\ && git pull \\ && git branch -v | grep \"\\[gone\\]\" | awk '{print $1}' | xargs git branch -D branch=$(git branch | grep \"*\" | awk '{print $2}') if [[ \"$branch\" != \"main\" ]]; then echo \"Left on branch $branch, manual cleanup may be required\" git branch exit 1 fi 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"}}