Friday, March 12, 2021

Python Script for Splitting a Delimited File Into Multiple Files

I needed to split a big file delimited by double line breaks into a series of smaller files. Python to the rescue!

Posting this because I didn't find anything on google that put together all the pieces I needed.

def split_file_on_delim(to_split_name, delim, splitted_base, splitted_ext):
    """This function reads a file, splits on delim, and writes each chunk out to its own new file."""   
    fn = open(to_split_name, "r")
    fr = fn.read()
    fs = fr.split(delim)
    for i, chunk in enumerate(fs):
        fw = open(splitted_base + "_%i." %i + splitted_ext,'w')
        fw.write (chunk)
        fw.close() 
#sample usage:  
split_file_on_delim("c:\\tmp\\to_split.txt", "\n\n", "c:\\tmp\\splitted", "txt")

Sunday, September 23, 2018

The Mythical Man-Month: A Review

Here's a link to my review of The Mythical Man-Month, by Frederick P. Brooks, Jr. I've been meaning to write one for a while and just now got around to it. Cross-posting here since it's related to programming.

Wednesday, December 20, 2017

Cities and Programs

Rome was not built in a day;
Neither is a complex program.

Both might have twisted, turning, needless complexities,
And yet the program is much more easily mended
Than the living, breathing, cobblestoned city.

Both can have hasty, ill-advised, poorly considered changes
(That often prove disastrous)
And sometimes work more by accident than by design.

Both ultimately devolve into disuse and ruin,
Living on in the memory of fewer and fewer
Until at long last, the last rememberer's long life
Is snuffed
softly
out.

Wednesday, October 14, 2015

A Software Engineer's Hippocratic Oath

I ran across this interesting article by Phillip A. Laplante on the responsibilities of software developers. More than a decade after it was written, many of Laplante's ideas and observations still ring true.

Here's his proposed version of the Hippocratic oath for software engineers. Even if it never becomes a part of the sacred and occult investment rites1 which every developer must undergo, it contains a lot of truth.
I solemnly pledge, first, to do no harm to the software entrusted to me; to not knowingly adopt any harmful practice, nor to adopt any practice or tool that I do not fully understand. With fervor, I promise to abstain from whatever is deleterious and mischievous. I will do all in my power to expand my skills and understanding, and will maintain and elevate the standard of my profession. With loyalty will I endeavor to aid the stakeholders, to hold in confidence all information that comes to my knowledge in the practice of my calling, and to devote myself to the welfare of the project committed to my care.
I especially like the idea of developer as a steward of things that have been entrusted to her for a time. After all, isn't that what most of our lives are about anyway?


1 Which I just now realized I probably shouldn't have even mentioned in so public a forum as this here Internet Web Log is. Whoops! Sorry, Programmer Illuminati.

Thursday, October 1, 2015

Error Creating Form: Ancestor for 'TFormName' Not Found

So, this one has been bugging me for a while. I'm using Embarcadero's RAD Studio 10 Seattle, working on a sizable C++ application. For a few specific units, whenever I load them, I get the following message:
Error creating form: Ancestor for 'TFormName' not found.
(Of course, it actually said the name of my form, not 'TFormName'.)

And when this happens, my .dfm (Delphi Form) won't load, so I can't do anything with the design of that particular form. To work around this, I had to open the ancestor (or in at least one case, multiple ancestors), and leave them open in the IDE, before being able to view my form's .dfm at all.

So I double-checked all my include and lib paths, as well as my include statements in the headers of my .h files. I googled my fingers to the bone, looking for any answer I could find. Nothing.

Finally, I noticed that the form I was trying to inherit from wasn't even in the inheritable items list, which led me to this old post, and I knew I'd found the culprit.

In the end, the solution was spectacularly simple: Remove all problematic units from the project. Then, add them back in the appropriate order. No more error!

It really was as simple as this:

Friday, August 15, 2014

Solved: Unable to open file 'FIREDAC.STAN.FACTORY.OBJ'

Today, I kept getting this error in Embarcadero C++ Builder XE6 when trying to compile a pretty large project:
[ilink32 Error] Fatal: Unable to open file 'FIREDAC.STAN.FACTORY.OBJ'
My project referred to a library that had FireDAC components, though I wasn't using any of them. My project uses DBX, not FireDAC, and VCL instead of FMX.

FireDAC.lib was in the project includes in my .cbproj file, so I had no idea what was wrong.

I finally found a fix: drop a FireDAC component (I used FDConnection) on a form. Save all, compile, and it worked. Then, I just deleted the FDConnection from the form and I was good to go.

Wednesday, July 16, 2014

Changing Fire Monkey (FMX) TextSettings at Run Time

One of my beefs with Embarcadero's C++ Builder is that it is so hard to find decent documentation or tutorials on how to do even the simplest things with their visual tools. This is especially true if you're developing in C++ instead of Delphi.

I experienced a pretty steep learning curve with Embarcadero's Visual Class Library (VCL) -- not because it is especially complicated, but because I couldn't find much good documentation for it.

Right now, I am building an FMX application, which is similar in a lot of ways to VCL. However, one thing has changed a lot: the styles are now driven by stylesheets.

For example, in VCL, it was relatively easy to change the font attributes of dynamically created elements at run time. Not so with FMX.

After several hours of fruitless googling, I still haven't figured out how to change my StyleLookup for a dynamically created TEdit. My custom style works fine for controls created at design time, but I'm apparently missing something from the picture.

After some trial and error, I did manage to come up with a solution that pretty closely replicates how text formatting worked in VCL. This obviously isn't ideal, but at this point, I will take what I can get and be happy to move on. The trick is to turn off the FontColor in your object's StyledSettings.


//Make ValueLabel to display output
TLabel * ValueLabel = new TLabel( this );
ValueLabel->Parent = VerifyMappingsScrollBox;
ValueLabel->Position->X = ValueLabelLeft;
ValueLabel->Position->Y = LabelTop;
ValueLabel->Width = ValueLabelWidth;
if ( OutputValue.Length() == 0 )
{
//Turn it light gray if empty
  ValueLabel->StyledSettings = ValueLabel->StyledSettings >> TStyledSetting::FontColor;
  ValueLabel->TextSettings->FontColor = claLightgray;
  ValueLabel->Text = "Empty";
}
else
{
  ValueLabel->Text = OutputValue;
}



So far, I'm not too impressed with the FMX framework. It does some cool things, but some elements are so buggy and counter-intuitive that it's easy to see why no one is making new documentation or tutorials for it. Which is a bummer for those of us who are obligated to use it for the foreseeable future.

Tuesday, May 13, 2014

Valid NPI Number Generator

If you're in the health care industry, you might be familiar with the term National Provider Identifier (NPI). This is a number used to identify medical providers in many various systems, including the SureScripts network.

NPIs are generated with a Luhn Check Digit, so you can't just dummy them up the way you can with other data elements, such as SSNs.

Here's a handy piece of JavaScript for helping you to generate the appropriate check digit for your NPIs. Just another reminder that other people have probably already solved most of the problems I encounter. :-)

And, for the benefit of future generations, here's the copypasta from that excellent blog:

var newNPI = ”; var base = this.value; var count = 0; var newCount = 0; for(i=base.length-1; i>=0; i–) { if(count % 2 == 0) {var tempNum = base.charAt(i)*2; if(tempNum >= 10) {var tempRemainder = tempNum % 10; newCount = newCount + tempRemainder + 1; } else { newCount = newCount + tempNum; } } else { newCount = newCount + parseInt(base.charAt(i)); } count++; } newCount = newCount + 24; var newMod = newCount % 10; var checkDigit = (10 – newMod) % 10; newNPI = base + checkDigit.toString(); alert(newNPI);

Wednesday, May 7, 2014

JavaScript Odometer Image Generator for Blogger

I looked around a LOT for a simple web app that would allow me to generate an odometer based on simple numeric inputs. Since I didn't find any, I decided to be like the Little Red Hen and make one myself.

It's not very fancy and doesn't have a nice UI or anything, but it gets the job done. If you have a Blogger blog, you can paste this into an HTML / JavaScript gadget and just change the variables in the "mileage" array to change the numbers that display. 

Here is the javascript I used to accomplish this:
<div id="odometer"></div>
<script>

//enter additional mileage in this array:
  var mileage = [ 136, // 04/05/2014
77.5, // 04/12/2014
259, // 05/03/2014
0 ];
var miles = 0;

while (mileage.length > 0)
{
miles += mileage.pop();
}

var totalmiles = miles;

    //handle weird floating point issue   
    totalmiles = Math.round( ( totalmiles + 0.00001 ) *       10 ) / 10; 
miles *= 10;

var digits = [];
var divby = 1000000;
var dig = 0;

while (divby >= 1)
{
dig = Math.floor(miles/divby);
digits.push(dig);
miles %= divby;
divby /= 10;
}

var bdigits = [ "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixfy7zp1130q44HasUzfTay4reYxWyqEp4w-TIzHLpwtUQqQLZRnEiXI80wwTZ25GaIRU1veSyoe3tTDbLjR64G4lta3j10tdTC1QyZniM3ILGc7i8qpVoKcqv3kLh9_l4W1p05k6b0fE/s1600/0b.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvxmrlnK_v9MC03SBlxVsHOXCPKu-fhTw_o2tEb5KVgO37BfiticDfdWy0kYrijw_un9BD0cQ9OQ5yPcuO4RgtEVKaT0dgqBX-9UITtauUuVDQmzuWsMOotPwZqf-aI4_Jxebmqd-OWVs/s1600/1b.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuLCPvFYoPZKuN-_elrJTzUjSR0je2lw-S47RJEo3iKDGCEvD1uAX0yZGdupaKIl-AEBtDZCwKeqM9pwAQMVI7RE2uoBZiIuuROWwgAOo_anvCISMQj1_w3mfyeakMSPtDn40buLGOcoc/s1600/2b.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNfvEKKrLRADzrxEEb59ZDwPQQz4eb1-IBAS5Adf-BnJEUUChZPuzcHv-nDNP2OzUgr0BrO2m_T6rBJtOnwxDkkXEjVjdwKWNi_mzVt5sKtVY0cmizPqiZasHC0Pyl-7Kcqeutf7hOx-k/s1600/3b.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-XoPOPA1hmkkLx2e3sXBgbbQh8oPFNQ8fb6nhnTVJ1rgzvnUaL3mxfvkHpTznphLsnq44yRQi0DAFG-9ocN6SM57b51Yj1TpEYisebz-brOfDVc2Xcn6AddY8slGbtr_xKKHkjxjHqFA/s1600/4b.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY9rOfRPgImbkqvaR-GJxz-QguQ1B_TdqoGAmP6w7ASen6_HyKINYcAjUu0JkFWoROwX6XVdKSAWYDtKS4YcLjNQ_ZZ75iT4_rbi-BU0PGDGr1rGF773_iYXrgyjwf0vF1JAKZuDQTwg8/s1600/5b.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeQNOe0iU9pzNVB2N4E5meC2WRvlbir4A30OpuDxN9qRtyyLW_nPK8Uzc8FSPUkBwr0B3S_F42AjnlEzejAM0afj-EdxhXGrEVZxAJqSmC9tbFEH2eQ8DnbJlmVJJU9ub6YUA3GD3ccw0/s1600/6b.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg74gad-rvzcdAnun9n2okYM23syAc0Z2GILDyFdx3BaTb45fZaZ5RduAwQEMhao9kVuwnROzN0kyrTWJhcd4dRZxaoWVuWsRLLZxO-a720byt-vUD0PQ3lmboPiKogPFjTtTA3Jsd0TMk/s1600/7b.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-xUk3aLuUrSHf8E2SnlYwfN0wjq_hQzdFAWuae3RIYZcxwuu9BOF1EACIys54FZeEHNbcCg0Ds_9k4kA6wy98RD3fW3v8Qhg19TdiA_UQXuqRNBfi3hvD6lQXtm6o5Y59yaVsI8LZFwU/s1600/8b.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgepHUQAZRTAFS40neIU4R6jQ0jAWw_VK9m-Pu9uqtt1x4hezd6E1GWYXcjrOf-5XP4p2Ykba6QNJUgSjRT3S3aMjJIE8yxCN6AM4pFL9VoPGDo45L7cjiewwZW0UKL-JSx3R9CzTXI1w0/s1600/9b.png"];

var wdigits = [ "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMizu_Etwhvi1UtuzbbiuVyxXV48zwN1dIAxlq1IUmsFGyXXGp7rYuAeIcqWj8pEY6Qv7kf4ZV2tyPT1CZJilf7mr8JTyiaNEXGtt6w51L_mBbFYZgS-QcgthErzx1svqtEPg-fnP-oVQ/s1600/0w.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkhtApucwCIjC_bsZEhTv1JyVvQ_NCBdpOjH0z-Biub3Q6-itCUJ1npqx8MOHqY_CWCmQcYRK0314uOgqOsOPJ15Hpa4_TEtQ4o1aymGG3v8CP8sgC41KSDk4XLbDW0CTou2V0XnS8VNw/s1600/1w.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFIclCnPvVTiH4nX08RoFSq081GqQ-rnj4Ixkrb_8YZemr6W0OL-Y7m9RZ5047nOsU_yl7gsIojAJh2NNPCkxffU_SGApI5xBnSsQs9h-aG2sZwvfDJ4B_V26J6JFZTIoqVFFOrPU2pUU/s1600/2w.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjx8vTAJJ7aHuEVUdhXtxd3rHr62lXJakamw8XGRJ2kkH-MDGm2c5OpEL5KUDKwln7ecyQfxMkE5GylIbzkRK9BbArbT2zm1bwIz-JI95-oTBeziMtzXok7IhLKR8zVlYpgjbzm7WrROyQ/s1600/3.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifIQl1f8KStYdl1gXJw1-JmrjVbMM_UETSQplTnU_vqdu5O8hgcKSwcHofykkWDrFLi2rOxxQgZVlTG5HKUsO5kJlAD_5R88EtsIYCtuNynDQNrKHked55Fdp0In5cd7RoAG5sZMUYCW8/s1600/4w.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhO0Y9WziM3vqsQHtQjHtLEBYXtgfhoZ3S_6TIDxiNB3KA3NXjZdUqS_w_XnKy7QjjzZL5m1laAmvkcVcMRwAm8ozx0wzaMmvqKPlPFmg0HKKVbLDTQ1EstF-iWK9Az7USjVU_Kpxbk4Q/s1600/5w.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4tw-8ONTf8Duf8kQfsV_AirXt7a9MCyPIp4aDcDA5OvQuPVAKs8DksMdJ2BC150adRP6lyE7WWdNzC_TxlmUKMdEKSCD-R2cUxa2oEzNp68FkK2T3zLbe5XUGeg_Vp2GZLrfjZYGdETQ/s1600/6w.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxafYXrWUs2N-QJ3kMUUopET3XKvAVNlbDfrrtuayIGhrr14fkavHVOmShaLy_DDDZ12KxhhS1NjgCaeMeMPyVU0VaU8Tcx9cvAah0Y3neMwhSGKNHxYl6E6ZDP1AoXsmb-sxnLerUVAU/s1600/7w.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6qUbGSpHT1zmarlruZzg2njnzWHCmp4TI8SL2MGdHIlH3elCvGR8Ldef5vwPjo9Eq3lRHSZKDM8JrziFb0tDD4AzY-D0cKrPgBSWdWUVSCOML0m0KUclDkdNW50hFQFKcRlxbwk3_9gM/s1600/8w.png",
"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUerBb544wgPIuJijBKFsS9nVxiatKuemhQ6YiPdd3QnkRGM4VRyJipcZzfhZMVUNCMbChJN23L0ES8gLB005ZUHIhyphenhyphenMTsCvBKcAd16cfJqm3fjQwCYLOvvL7X9AU0mzkqK60Y9WEvmrw/s1600/9w.png"];

var borderimg = "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMZKzzR5pqGpcd5ouTVF80cB49RxSdFMAvyhhqcLV274PL6zelk0EgrsxF3owl2By94Iea_wH4laF7G1FUHKHKOvXUF49oxmOIr7o6pUmdPGu0HSc1ilEu1gFkxGCLXoAyih9fRmXPJNY/s1600/border.png";

var outputHtml = "<img height = '32' title = '" + totalmiles + " miles' src = '" + borderimg + "'/ >";

for (var x=0; x<digits.length-1; x++)
{
outputHtml += "<img width = '20' title = '" + totalmiles + " miles' src = '" + bdigits[digits[x]] + "'/ >";
}

outputHtml += "<img width = '20' title = '" + totalmiles + " miles' src = '" + wdigits[ digits[digits.length-1] ] + "'/ >";

outputHtml += "<img height = '32' title = '" + totalmiles + " miles' src = '" + borderimg + "'/ >";

document.getElementById("odometer").innerHTML = outputHtml;

</script>

Thursday, April 10, 2014

Need to View Files from your Chrome Cache? Here's a Solution!

Here's a great solution to a problem that I was about to solve the hard way. I was trying to retrieve some images (just the images) from a web page I had visited in Chrome.

(They were wrapped in a design that wouldn't allow just right-click, then save as. And, yes, I had a good reason for needing them... it's a long story regarding my church's flaky web provider.)

In Chrome, if you type "about:cache", it brings up a list of the files the browser has recently loaded. When you click on them, though, you see a large HTML file with a hexadecimal code in the body. This is true even for binary files like .jpg or .png.

Senseful Solutions has posted a great little tool for translating the hex back into pages (or, in my case, images): Viewing Chrome cache (the easy way).

This is what programming is all about: making people's lives easier by creating elegant solutions to everyday problems.

Tuesday, April 1, 2014

XML Formatting in Edit Plus

I was thinking of writing a macro to help me format XML in Edit Plus. Fortunately, someone else already came up with a better solution 5 years ago.

In case this link ever dies, here's the code he used:
var stdin  = WScript.StdIn; var stdout = WScript.StdOut; var xml = WScript.createObject("MSXML2.DomDocument"); xml.loadXML(stdin.ReadAll().replace(/\t/g, '').replace(/\r|\n/g, '').replace(/\>\</g, '>\n<')); stdout.Write(xml.xml.substr(0, xml.xml.length-2));
This is why I love the internet.

Scheduling for Makers

Here's a great essay by Paul Graham: Maker's Schedule, Manager's Schedule.

One of my favorite parts:
When you're operating on the maker's schedule, meetings are a disaster. A single meeting can blow a whole afternoon, by breaking it into two pieces each too small to do anything hard in. Plus you have to remember to go to the meeting. That's no problem for someone on the manager's schedule. There's always something coming on the next hour; the only question is what. But when someone on the maker's schedule has a meeting, they have to think about it.

For someone on the maker's schedule, having a meeting is like throwing an exception. It doesn't merely cause you to switch from one task to another; it changes the mode in which you work.
This shines a lot of light on some of the most frustrating parts of my previous job as a Business Systems Analyst. I was never happier than when I had a meeting-free day to work on my deliverables and help my developers with theirs. The problem was that I was also expected to attend a lot of planning, project management, and status meetings that shouldn't have been in my job description.

Meetings interrupt creative and analytical processes. They use time that would be more efficiently spent on solving problems. Sometimes they are a necessary evil, but often they are just plain evil.

I wish every manager and every scrum master would read Graham's essay, digest it, and internalize the lessons. It would make a world of difference to the people who work with and for them.

Thursday, March 27, 2014

I Love DeBugger

I'm not sure how I ever got along without using my compiler's debugger. It's SO much nicer to see the call stack and step through my code rather just making wild guesses about what might be wrong.

I am reminded of Winston Smith's epiphany at the end of 1984 (though, I hope, without the sinister overtones). With apologies to George Orwell:
O cruel, needless misunderstanding! O stubborn, self-willed exile from the loving breast! Two gin-scented tears trickled down the sides of his nose. But it was all right, everything was all right, the struggle was finished. He had won the victory over himself. He loved DeBugger.

Wednesday, March 5, 2014

Baby Steps

I am working on a pretty big C++ project to take XML, parse out certain fields in it, and then use it to populate various tables.
My first few efforts in writing this class were akin to giving a mouse a drink from a firehouse -- extremely frustrating, and not particularly enjoyable or profitable. It dampened my fur, to say the least.

But then I remembered some of the best programming advice I ever received, and that has helped things to go better.

When in doubt, remember Bob's method: baby steps. It'll make you feel much better.

Tuesday, March 4, 2014

The Pragmatic Programmer: Craftsmen and Cathedrals

I'm still only in the preface to Andrew Hunt's and David Thomas's The Pragmatic Programmer, but I can already tell it's going to be a favorite.

Hunt and Thomas have this to say about about the importance of craftsmanship in software development:
Think about the large cathedrals built in Europe during the Middle Ages. Each took thousands of person-years of effort, spread over many decades. Lessons learned were passed down to the next set of builders, who advanced the state of structural engineering with their accomplishments. But the carpenters, stonecutters, carvers, and glass workers were all craftspeople, interpreting the engineering requirements to produce a whole that transcended the purely mechanical side of their construction. It was their belief in their individual contributions that sustained the projects.
We who cut mere stones must always be envisioning cathedrals.

-Quarry Worker's Creed  
 (p. xx of The Pragmatic Programmer, Boston: Addison-Wesley, 2000 )
Good stuff -- makes me want to do more than just sling code that compiles. It's an excellent reminder that there is always room for beauty and elegance, even in the seemingly mundane.

Monday, February 3, 2014

Stuck? Review, Debug, Rebuild.

Just finished troubleshooting a very irritating bug.

In the process, I ran up against a couple of walls and got pretty frustrated. I ended up just setting it aside for awhile, since I was out of strategies to try. I think this was the best course of action for someone with my admittedly limited experience.

I asked a senior developer about it this morning, and he encouraged me to follow this flow in the future.



















I don't know if it was something funny about my IDE or just the standard n00b problem of missing some important steps, but my current build of the project was pretty screwy. This caused an error when my C++ app tried to execute a script in SQL Server. Cleaning and doing a full rebuild was just the ticket.

So, when I don't know what to do next, I'm adding "full rebuild" to the list of options.

Wednesday, January 29, 2014

I've Got 99 Problems... and I am the Solution!

Until very recently, I was a business systems analyst (BSA) at a large financial services company. In changing career paths, I have noticed that application developers have to think very differently from BSAs.

Typically, a BSA's role is all about requirements, details, and approvals. Some problem solving is required, but a BSA's problems tend to center around people. In that position, I was always reasonably confident that the answer was out there. I was also pretty good at knowing how to find it.

As a developer, I am finding that sometimes, the answer isn't out there. My job is to create the answer, not document one that already exists. This is simultaneously my favorite and my least favorite part about my new role as a programmer.

Very little frustrates me as much as attempting something that should be simple -- such as getting my code to compile -- and running into wall after wall without any success.

And very little is more exhilarating than finding an elegant solution to a difficult problem.

Despite the occasional moments of wanting to bash my head against the wall, I think I'm going to like this job. It's a fun change to be the one creating answers instead of the one who records them.

Monday, January 20, 2014

Lucidchart: A Great Replacement for MS Visio

I was looking for an online flowchart utility and found this web-based gem of an application: Flow Chart Maker & Online Diagram Software | Lucidchart.

Lucidchart has an impressive number of features that are attractive to me (and, I suspect, many other users):
  • Integration with Google Drive
  • Intuitive user interface (Visio is nowhere near this good at UI design)
  • Cloud-based storage
  • Excellent collaboration support
  • Tier-based, feature-driven pricing model
They aren't paying me anything to say this; I tried it and really just think it's a terrific app. The clean, modern website design is also notable and gives them big marketing points, in my book.

I am looking forward to using this app more... I hope it lives up to my initial good impression.

Friday, January 17, 2014

Change

Whoever said that change is good probably wasn't a programmer.

I don't know about you, but if my code is running well and isn't eating up too many system resources, I don't want to change it. This is particularly true for purely stylistic changes. For example, I am seeing a lot of code like this in a program I've been reviewing:

if(a == false){    Do action;}

if(b == true){    Do action;}

This syntax isn't going to throw any errors, but it bothers me because it's not good idiomatic C++. A better (and equivalent) way to express these statements would be:

if(!a){    Do action;}

if(b){    Do action;}
If this was a novel -- or even one of my students' papers -- I would slash away with my red pen. With production code, however, that can be dangerous.

Yes, cleaning up legacy code can improve readability and, in some cases, even performance. But the risk of bumping or forgetting something in the process is greater than any benefit I can imagine from making the change.

Bottom line: in written or spoken English, I am quick to assert my preferences. The risk is low and I have the experience to back my preferences up. In production code, though, my motto is closer to "If it ain't broke, don't fix it."