This morning I referred to my eggs as "hard coded."
Using data from external files
I am working with some code that processes CSV files. Each row corresponds to an existing record and the record is updated in response to the column values. This is not an uncommon task. The existing code implements this in an also not uncommon way by intermixing row parsing and record updating. For example, assume we are updating Foos
class Foo attr id, :location, :valuation # ... endA typical intermixing is
row = [...]
raise "unusable foo id" if row[0].blank?
foo = Foo.find(row[0].to_i)
raise "foo not found with id #{row[0]}" unless foo
raise "unusable town location" if row[1].blank?
location = Location.find_by(town: row[1])
raise "location not found with town #{row[1]}" unless location;
foo.location = location
raise "unusable valuation" unless values[2].to_i < 10_000
foo.valulation = values[2].to_i
While this initially seems like a reasonable approach it quickly breaks down as the number of columns increase, column format is non-trival, and there are column (or row) interdependencies. But the more significant problem is that the parsing and the updating can't be tested individually. This makes the test harder to write, understand, and maintain.
It is always better to first parse the raw data, validate it, and then use it. Eg
class Record
attr :id, :town, :valuation
attr :foo, :location
def initialize(values)
raise "unusable foo id" unless /^\s*(\d+)\s*$/ =~ values[0]
id = $1.to_i
raise "unusable town location" unless /^\s*(.+)\s*$/ =~ values[1]
location = $1
raise "unusable valuation" unless /^\s*(\d+)\s*$/ =~ values[2]
valuation = $1.to_i
end
def validate
foo = Foo.find(id)
raise "foo not found with id #{id}" unless foo
location = Location.find_by(town:)
raise "location not found with town #{town}" unless location
raise "valuation does not match the minimum" unless valuation >= 10_000;
end
end
# read the raw data
rows = [[...], ...]
# parse and validate the data
records = rows.map do |row|
record = Record.new(row)
record.validate
end
# use the data
records.each do |record|
record.foo.location = record.location
record.foo.valuation = record.valuation
record.foo.save
end
This parse, validate, and use approach is approporate for all cases where you are bringing data from the outside into your application, no matter what the outside source.
ps. These small, helper classes are your friends. Prefer them over your language's hash primitive as they provide great control. Most languages have efficient syntax for creating and using them.
Short list of useful Mac utilities
The Paris Olympics' pictograms
Remain in control of your runtime
Maintenance, evolution, and technical debt are different
I was listening to a recent Stack Overflow podcast on technical debit and was getting progressively annoyed. The cause of the annoyance was that the speaker was lumping all changes that were not feature changes as technical debit. The changes he was speaking about are better characterized as either maintenance, evolution, or technical debt.
Maintenance is bug fixes. The original implementation is deficient and needs to be repaired. This work is only related to the original design and current feature set.
Evolution is change needed due to alterations in the runtime environment. This work is only related to the original design and intended runtime environment. For example, you need to update a library and that results in a need to update the original implementation.
Technical debt comes about when the original design is unable to accommodate a new need. For whatever reason, it is no longer suitable.
Too often "technical debt" is used to mask an organization's lack of timely maintenance and planned evolution. Don't do that.
Naming things once
How is x = %i[a b c] better than x = [:a, :b, :c]? I find these kinds of shortcuts available in Ruby, and exploited to the maximum in Rails (et al), a severe detriment to understanding and exploring the code. Thankfully, JetBrain's RubyMine is almost always successful handling these pointless alternative codings. Ruby & Rails: Preventing successful grep-ing since forever.
Update: This is a somewhat related discussion on Hacker News, Greppability is an underrated code metric.
Understanding Patterns of Project Behavior
Adrenaline Junkies and Template Zombies: Understanding Patterns of Project Behavior is a useful catalog of project specimens and anti-specimen. As a catalog it does capture much of what I have experienced (and participated) during projects. Like Brook's The Mythical Man-Month, the information contained is, unfortunately for our industry, evergreen. I would not categorize this as a pattern or anti-pattern catalog, however, as, unlike good patterns, it does not regularly offer solutions. Nevertheless, its is worth having around if only to remind yourself that you are not crazy, the project is.
Why can't we validate a branch locally?
I have been on several projects where a developer's version control branch needs to pass continious integration (CI in CI/CD) on a remote service before it is eligible to be merged. The implication here is that a successful remote test run is sufficient validation for acceptance. But given that the remote CI environment is also unlike the production environment I have been wondering why do we accept this validation over a local validation?
It seems to me that an organization should strive to enable local validation over remote validation if only for its cost benefits. Central CI environments are costly to operate. A common consequence of this is that these costs are reduced by having several test runs share an environment. Tests fail not due to a developer’s negligence but to the missing isolation. Validation now requires multiple runs in the hope that the next one will avoid all the conflicts and pass. It is CI wack-a-mole style, a style that costs too much and delays feature releases.
All developer machines are capable enough to run CI in the background while development happens in the foreground. The only thing that is missing is a signifier that authenticates the successful run. Eg, a value tied to the code commit, the successful test run log commit, and the environment commit.
SQL Antipatterns: Avoiding the Pitfalls of Database Programming
Future waterfront property
My town has many flooded roads, parking lots, and parks today. (We had 2.5" of rain in 12 hours.) My neighbor once joked that with climate change he and I have future waterfront property. It is becoming truer every year.
Sol Rashidi on delivering an AI product
This interview Your AI Survival Guide • Sol Rashidi & Joe Reis • GOTO 2024 is a useful discussion on delivering an AI product.
I read Sol Rashidi's book Your AI Survival Guide. While it is focused on delivering AI projects, it is also her distinctive take on project teams and delivery in general. Chapter 4, "How to Start", is very good and worth reading alone. I especially liked the quantitative readiness assessment as a means of selecting whether the business should be attempting an efficiency, productivity, effectiveness, expert, or growth AI strategy.
Inspiration Milestones
For many software and hardware engineers of my generation the 1981 issue of Byte Magazine coverage of Smalltalk and the 1991 issue of Scientific American covering "ubiquitous computing" were defining milestones.
Vote in favor of the High School bond
South Kingstown needs a new High School. The current building is too old, too big, and too costly to maintain or to renovate. For three years the Town and the School Committee have been working toward a replacement. This is a long process but one that has been done with much commununity eguagement and open meetings. At this time we have the benefit that the Rhode Island Department of Education will contribute $81M towards the new school. This contribution, however, is not forever and we risk loosing it if we don't act soon. The $150M cost is a better estimate than the $125M of just a year ago. (Construction costs in RI are very high due, in part, to a labor shortage.) The $150M will be borrowed over 4 years and paid back over 20 years. For the average house in SK that means approximatly a $60/year increase in taxes until all the money is borrowed for a final $318 increase in taxes for the remaining period of the loan. (Ie, $60, $120, $180, $240, $318, $318, ...) For my house that works out to be 3 weeks of extra tax payments, hardly something to be concerned about.
With that said, this $318/year is in addition to the normal increase in property tax. The tax increase is between 1.2% and 1.7% per year. SK runs a very tight operation. (Narragansett, for example, is at 4%.) For my house that's a $60 to $80 per year increase. So, in 4 years I will be paying several hundred dollars more in taxes. I am not indifferent to this being a significant increase for those on a fixed income. (It will not be long before I am amongst them.) But it is also far less than other communities are seeing.
South Kingstown continues to attract new residents and tourists from all around New England. Our tax base is growing and, if we are prepared, we can increase our commerical as well as residential tax base. There is much reason to expect our tax payments to remain well within an acceptable amount for all the benifits we have living here. If my house was in Richmond, just a few miles inland, I would be paying 50% more in property taxes. I much prefer living just minutes from the ocean. I suspect you do too.
Please vote in favor of the High School bond. You can vote at the Town Hall now or on May 7 at your polling station.
Update: I have seen and heard statements that taxes will increase by over $1,000 by 2030. The implication being that this increase is due to the High School bond. The reality is that over $700 of that increase is just normal increases in Town and School spending.
Inspiration and ingenuity
Sometimes all you need is inspiration and ingenuity for your tabletop space wargames.
PDF and filling in forms with pdfcpu
I am always on the lookout for programatic PDF tools. I have heard for a very long time that PDF is dying and yet it is still far from dead. Pdfcpu is a newish tool that works on the command line. I was interested in it as it has a simple mechanism for filling in PDF forms. It uses a JSON file to provide the form data, and that along with the original PDF is then used to create a new filled in PDF.
Use the form export sub-command once to get the JSON structure
pdfcpu form export in.pdf out.json
This JSON file would be used to create a programatic template. Edit or generate the JSON with the form data you want. Now create the filled in PDF.
pdfcpu form fill in.pdf in.json out.pdf
What got me particually interested in it is that it is written in Go, which I am currently learning, and is open sourced. Hopefully, someone is thinking about turning form filling into a lightweight, locally running HTTP service.
Natural RGB
I really enjoy the Tippecansett trail in the Arcadia management area. It is strenuous and beautiful. Some of yesterday's colors, a natural RGB,
Reading AI research papers, etc
I found this useful, particularly the categorization of papers into surveys, benchmarks, and breakthroughs. The presentation is only a few days old and so the specific papers listed are representative of the current state of art. As a CS Phd friend of mine once told me when I was struggling to understand an algorithm proof, as a practitioner you really don't need to understand the proof, just the results. I suspect that is true for these papers too.
How To Read AI Research Papers Effectively
https://www.youtube.com/watch?v=K6Wui3mn-uI
At local meetup I mentioned that several years ago Google had released hardware that targeted machine learning. I could not remember the details. As luck would have it, I listened to Jeff Dean's presentation this weekend and he mentioned the Tensor Processing Unit (TPU) for low precision linear algebra. Overall, the presentation is interesting and useful. The majority of it is focused Google's Gemini/Bard, but given who the speaker is this is understandable.
Jeff Dean (Google): Exciting Trends in Machine Learning
https://www.youtube.com/watch?v=oSCRZkSQ1CE