HOME     SITEMAP     RSS     TWITTER     EMAIL    
Search:   

FollowSteph Follow Steph as he posts Blog Blazer Friday
 

Archive for the 'Programming' Category

Printing is Broken on Mac OS X with Java 7

Confused Programmer

Printing on the Mac OS with Java 7 has been broken for a long time, months in fact. I don’t understand how this can possibly happen. After all printing isn’t exactly a small issue, and the Mac isn’t exactly a small market. So how can this be? For months? I don’t get it…

Specifically the font attributes are not honored on the Mac OS. Java seems to make the proper OS calls but for whatever reason the Mac OS just ignores those calls. Here’s the official bug report. To quote:

“Call to MacOS native function CTTypesetterCreateWithAttributedStringAndOptions did not produce the expected result.  Although the font dictionary was passed in options, for unknown reason, it is ignored.  Fix is to add the font dictionary in the attributed string and use CTTypesetterCreateWithAttributedString.”

And this only affects the Mac, it works fine in Windows or with Linux. The following code shows how to replicate the Java 7 printing bug on the Mac OS:

Font font = new Font("Times", Font.BOLD, 24);
Graphics2D g = (Graphics2D)graphics;
g.setFont(font);
g.drawString("Hello World", 10, 10);
// Increase font by 10 - Mac OS ignores this next line of code.
g.setFont(font.deriveFont(font.getSize2D()+10));
g.drawString("Hello World in a bigger font", 10, 100);

The weirdest part of this whole issue is that it’s really only broken in Java 7!! Of course it’s the only stable release of Java. How does that make any sense? It still works on Java 6 which is no longer supported (past it’s End of Life). For Java 8, which is still in beta and does NOT have a stable release, it seems to be working. However for Java 7, nope. Nothing. Nada. Huh???

In case you want to read further information from the better links I found on the issue, they are here, here, here, here, here, here, here, here, here, here, and here. Just a few links. So it’s not exactly an unknown issue. Many people are struggling with it.

That being said, several people have found workaround solutions, including us here at LandlordMax Property Management Software. A common solution I’ve heard is to create an image of what you want to print and print that image. It makes sense and it will work because you’re bypassing the font attributes. I’m not a big fan of this solution, especially since it doesn’t really scale well to multi-page report printouts that we need in LandlordMax.

So what we’ve been forced to do is create a temporary PDF file of the report, ask the Mac OS to print the file using the system command lp filename.pdf, and then delete the temporary file. This works pretty well, the only downside is that the Page Setup and printer setting options that comes along with most programs are not available because we’re not really printing from the software itself, nor can we pass them on to the system command. It’s not ideal, but it’s the only way to print within Java 7 on the Mac OS.

We added this workaround in the software with Java 7 Update 17. Unfortunately s far as we can tell the bug has been around at least since Update 9, but probably earlier. I just checked and we’re now at Java 7 Update 25, and guess what. Still no fix! No mention of the issue either.

The big question now is whether this is a Java (Oracle) issue or a Mac (Apple) issue? In either case, I would think both companies should want to fix this issue right away. It’s not exactly a minor issue. I don’t understand what’s holding up a resolution to this issue. It makes absolutely no sense to me. This is core functionality that affects both their users in a big way. It’s not a little bug, it’s huge! Printing is important, even in today’s digital world.

Hopefully Apple and Oracle can put their differences aside for a little bit and resolve this issue. It’s affecting a lot of people and we don’t deserve to be the collateral damage to their war against each other.






Lines of Code Revisited Again

LandlordMax Rental Property Software - Lines of Code

Again, just like last time, I completely agree that Lines of Code (LOC) is not an ideal metric to measure the scale of a system because of all the varying factors. However this is all I have so I’m going to do the best I can with what I have.

As many of you already we’re hard at work on a networked and multi-user version of LandlordMax to which you can see the daily status update here. It will be released as a minor update, that is a letter increment rather than a full version number increment. The main reason for this is to assist people in moving up to the networked version with the lease friction and costs possible.

In any case, I thought I would share with you today the scale of this effort. Yes again I do realize LOC is not the best metric, but it’s really the only metric I have which can show the scale of the effort. As you’ll see, the amount of new programming code included between each version is fairly close. This is NOT intentional by any means, and I hadn’t realized it until I generated this chart today, it just happened that way. Anyways as you’ll quickly see, the difference in LOC between the last version, version 6.05d, and the upcoming version 6.05e is about the same as a full version number release!

Of course a lot of that effort is specific to the networked offering and the as such the desktop offering will only get some of the benefits, but regardless it’s still a major effort.

All that to say the upcoming networked version 6.05e is a major undertaking, probably more than most people realize. Offering networking capabilities along with multi-user support is definitely a lot more complex than just letting multiple users connect to the same database, especially when you’re dealing with complex data that many of the users connected to the same system at the same time will be sharing and modify on a regular basis. You have to deal with a lot of synchronization issues, especially when it comes to modifying and saving existing data which I won’t get into today.

And if anyone has any suggestions on other interesting metrics I can fairly easily, please let me know. I’m always interested in learning more about our system.






Are Your Backups Actually Good?

Computer Failure

Being the founder of a property management software business (LandlordMax), I can’t tell you how many times we’re contacted by people who’ve lost their data because of a hard drive failure, a complete computer failure, a virus, and so on. It happens all the time. So much so that a few years ago I wrote 4 Simple Steps to Protect Your Data From 99.9999% of all Computer Failures to help prevent this from happening to as many people as possible.

The good news is that today I’m seeing a lot more people pro-actively backing up their computers and their data. The bad news is that not all solutions are good. Whatever your backup solution is, you should test it before you NEED to use it. You might be surprised at how exactly it works. Or maybe it just simply doesn’t work. Maybe the automated backups aren’t actually backing up anything. Maybe it’s backing up the wrong files. Maybe the software you’re using is faulty. Whatever backup method, test it.

To give you an example, I was recently talking to a customer who was very active in her backup procedures. She knew that backing up was important, and she was very actively using a service to remotely backup her data in real time. I highly commend her for that, that’s better than most people. That’s exactly what we all want to see. I can’t praise her enough for being pro-active. And because of this she felt confident that her data was safe, which is completely reasonable, I would too.

However there’s one very big issue, and maybe you’ve already spotted it. If not, re-read the previous paragraph. Can you see it now? Her data was backed up in real time! If you think about it, this only protects you from a hardware failure or theft at best. And even then, if the harddrive is bad, you’ll still have the bad (corrupted) files overwrite your good files!! It only really protects you from a computer failure that’s very quick (power supply that shorts the machine) or theft. Maybe a few other situations, but it’s very limited. It doesn’t protect you from a bad harddrive, you’ll just push the same bad data to the backup service as the files get corrupted. It doesn’t protect you from a virus, the virus is just pushed over to the backup. It doesn’t protect you from accidentally deleting a file, the file is just as fast deleted on the backup!! Real time backups are good for backing up you system as it is exactly right now, good AND bad!

In other words, real time backups can be very limiting unless you can revert to a previous day, week, or month. And because most of these services are low cost, they don’t really offer these options. They just can’t, it’s not economically possible. For example, the solution use by the previously mentioned customer charges $54.95 a year for real time backups. If you look at the numbers, that’s less than $5/month for unlimited storage. I did notice that in her case the bandwidth was really slow, an 8MB file took about 15 minutes to restore. I would also assume support is about as good as $5/month hosting. But ignoring that, how can they feasibly offer tagged (dated) backups at those prices? Would most consumers pay $100/year for backups? My guess is probably not. Which means you won’t be able to revert to a previous version, just to your current version, whether it’s good OR bad.

Which means that if you overwrite a file, it immediately overwrites your backup. You can’t revert. You can only get what’s on your  disk right now. The same is true if you’re infected. All you can do is get back the infected files. The only time it will save you is if you’re computer dies suddenly due to a hardware failure, theft, or other even less likely events.

So the moral of this post, whatever your backup solution is, I strongly recommend you verify it before you NEED to use it. You may be in for some surprises. The backup disk may not work. The backup system may not actually be backing up anything. Can you get a previous backup that’s not from today (in case you have a virus)? How long will it take you to get your computer back up (at 8Mb/15 minutes, a 1 GB backup could take days!!)? Don’t just think because you have a backup solution that you’re good to go, test it!






Who Should Make the Decision?

Make Decision

Is it the person in charge that should make the final decision? Sometimes, but not always. It should be the person who’s most knowledgeable with the information on which the decision will be based.

Let me give you an example. In the tv show 24, this season the president of the United States was taken hostage in the White House by a small team of armed men. Yes it’s quite a story, but let’s ignore that for now. What was interesting is that the decision on whether or not to storm the White House and retake control was not decided by the agents that were following the case the whole time, those that knew who the terrorists were and had studied their histories, those that really understood their motivations and intentions.

Instead the decision was taken by the Vice President. He didn’t even know what was going on, he was just quickly appraised of the situation, well as much as they could within minutes, and he had to make a decision on whether or not to storm the White House. There’s no way this person could make the right decision unless it was pure luck. There’s was just too much history and information needed to full appreciate and understand the situation. Plus, he had additional motives (such as protecting himself) which came into play. In this case the wrong person to make the decision was the person in charge.

Of course this is only a tv show, but it happens all too often. Especially in software development. I can’t tell you how often I’ve seen developers struggle and fight through a hard problem only to come to a fork in the road. They then have to go to the team lead / manager (or whoever is in charge, which is sometimes someone who’s not at all technical) and have to explain the issue. Now if it’s a good lead, they will listen to the developer and pretty much let them lead the decision, after all they’ve been in the thick of it while the leader only superficially knows and understands the issues. It makes sense and is the smart thing to do.

However many times the opposite happens. The suggestion is completely ignored and the developer is told to just make it happen (even when they strongly disagree and push against it). My favorite is “I understand you might not think this is a good idea, but just do it anyways.” The reality is that someone working on a problem for hours or days can’t really express the full issue in minutes. It’s just not possible, so what you get are the highlights, the glance over. If you really want to make the decision, than dig into it, spend some time to really appreciate what’s going on. Expect to spend some hours, maybe even days to fully understand what’s going on. Especially if the decision has large implications for the future.

The reality though is that sometimes the developer is not perfect either, they might not appreciate everything else that’s going on. Maybe there’s political factors. Maybe there’s monetary factors. Maybe some timelines with the marketing team that need to be met, etc. If you can (I understand that sometimes it’s not possible), than share with your developers other factors that will affect the decision. Help them rather than just make the decision outright based on only one factor. In other words, don’t just pick option A because it will allow you to meet your deadline while option B won’t because it might come back to bite you very hard. If the developer is pushing option B so adamantly, figure out why. And only then make your decision. Don’t just base it on one factor that you know, understand the full problem. Work together to come to a decision.






Sometimes Simple Things Aren't So Simple

LandlordMax Property Management Software: Tenants

As some of you already know, we’re working very hard to release a new version of LandlordMax Property Management Software in the near future (within weeks hopefully). As you’d expect, you’ll find lots of new and exciting features.

However not all features we wanted to include will make it into this version. For example, we tried to squeeze in a feature to give you the ability to “archive” older data. To give an example, let’s say you have a tenant “John Smith” that’s moved out some time ago, you might not want to see his name appear in the drop down list of tenants anymore. At the same time, you don’t want to delete him because a lot of older data is associated to him (such as all his rents, etc.). Therefore what we came up with is the ability to archive data such as tenants.

Seems simple doesn’t it? Should be easy. But it’s not as simple as it first appears. At least not if you want to keep your software easy to use!

Although I promise I’ll try to keep it as un-technical as I can, I’ll need to be a little technical to explain why it looks so easy at first glance. The obvious changes are that we need to add a property to the tenant to mark it as archived (in both the database and the code). Next we need to add the ability to mark a tenant as archived within the screen with a simple checkbox. Nothing too complex yet. But that’s where the simplicity ends.

You’re probably asking yourself how can it become complex from here. Really, all we have is a checkbox to mark an item as archived. If it’s archived, don’t include it in the list. Simple. Not so. Firstly, in your main list of tenants, do you display both archived and non-archived tenants? If you say just display non-archived tenants you’d be wrong. Before I give you the answer why this won’t work, ask yourself how do you edit an archived tenant if you only list non-archived tenants?

Aha! You can’t! Therefore you have to give your users the ability to see both. But then if you do, you defeat the purpose of having the ability to archive tenants. Well the solution we came up with is to include an additional filter at the top of the list. This way you can see all tenants, or only non-archived tenants. By default you’d of course show all tenants because not everyone will know there are filters when they start to use the software. And if you’re smart, you save the filter settings so that the user doesn’t have to reset it each time they go back to the list view.

That wasn’t so difficult. Of course if it ended there I wouldn’t be writting this would I. The next issue we have to deal with is what happens if someone tries to archive data that shouldn’t be archived. For example what happens if they try to archive a tenant that’s currently living in an apartment? Do you let them? Probably not. So now you have to create rules as to who can be archived and who can’t. Is it as simple as just allowing tenant’s that aren’t currently living in a unit? It would be nice if it was, but it’s not. It’s possible that a tenant has a lease to a unit that they’re not living in (a parking spot, a parent leasing for their child while away at college, etc.). So you can’t assume this. But for now, let’s assume you can ignore all this and just not let a user archive a tenant that’s currently in a unit (we’ll deal with the other issues later).

Have we solved all the issues? No, not yet. In our software we offer a dropdown (combobox) list of tenants on the other data entry screens. So for example, on the workorder screen you can select a tenant from a drop down list for that workorder. This makes life easier as all the tenant’s info is associated with the workorder (for reporting, printing, etc.). By doing this, we’ve just solved the issue of keeping the list of tenants to only relevant tenants (ie non-archived tenants). This is great.

But what about reports? How can we generate reports on archived tenants? Based on our current solution it’s not possible. For a user to generate a report on an archived tenant, they’d first have to un-archive that tenant, generate the report, and then re-archive them. Not very user friendly is it? And if we didn’t care about making life easier for our customers we could do just that. But of course we care, so that’s not an option. This means therefore that we have to alter all the reports that let you select a tenant to give you the option of listing all tenants or all non-archived tenants. Nothing too major, but we also have a lot of reports.

We’re still not done. What data do we use on our reports? On some reports we want to use archived tenants and on others we don’t want to use archived tenants. For example, on the reports that list all accounting entries or cashflow, we want to show all accounting entries regardless of whether or not the tenant is archived. However, when we display a list of tenants, we may or may not want to include archived tenants (another option the user needs to select). The same is true for reports on security deposits, leases, etc. So we need a way to toggle whether or not to include archived tenants.

What about reports grouped by tenant? Again same issue, we need a way to ask the user if they want to include archived tenants or not. Is just asking whether or not to include archived tenants enough? Unfortunately it’s not. Within some reports included in LandlordMax you have the ability to include a date range. For example, you can generate a rent roll report between any start and end date. You can generate a list of leases that will expire within a start and end date. Why am I mentioning this here? Isn’t it just as simple as including the option to include archived tenants or not? No. Well, yes, technically we could ignore this and just leave the responsibility to the user to deal with their own issues.

What do I mean? If someone selects to only display non-archived data isn’t that what they really want? Maybe not. Let’s say I want to generate a report that will list all “Accounting entries grouped by tenant” for last year. What do you think will happen? I might be missing half my data because half my tenants are archived. What about for the year 2001? Odds are pretty high that many tenants would be missing. Do we just ignore this use case and let the user deal with it. Never mind the support requests we may get from people complaining that many of their tenants are missing, especially since the data would appear on reports such as “All accounting entries”.

Assuming you’re still here and reading through this longer post, you can now appreciate how sometimes a simple feature can quickly escalate into a larger and more complex feature. In our case, we were hoping to squeeze in this feature for the next major release but I’ve had to make the decision to push it off. If we don’t, we have to address all of these issues. Well maybe we don’t, but if we want to maintain that we’re the easiest we definitely need to. At least I can’t knowingly release software with these glaring issues incomplete.

I can understand releasing software that’s fully working but missing some features (all software is like that, we can always add more features). For example offering the ability to send emails within the software but not offering a spellchecker is not that terrible (don’t worry we’re going to offer a spellchecker in the next major release). Your users can still send emails, they just can’t spell check. It’s not fun, but it works as expected. However releasing a feature that can allow your users to go into an unstable state (archive tenants that are currently renting your unit) or cause unexpected results is not good. Even if the software is behaving as it should, if the behavior isn’t what people expect there’s an issue.

And because of all this, the ability to archive tenants and other data has been pushed off. Especially when you consider the cost to benefit. The benefit of this feature is that the user doesn’t have to scroll through a longer (sometimes much longer) list of tenants that are no longer relevant. Yes it would be great to shorten this list, and I agree with the people who requested it, it is a much needed feature. But at this time the costs are too high and we’re too close to our release date. These are the hard decisions that no one wants to make but that must be made.

PS: If you ever wanted to know why software is sometimes delayed, this is a perfect example. This is a feature that seemed simple at first inspection but wasn’t. Actually even after some thought it still didn’t seem that complex. It wasn’t until we really started to implement it that we understood the issues and the full scope. And nor could we, how could you know ahead of time. In retrospect it’s easy, but think back to when you first started reading this post. Did you even have the faintest idea of what was coming up?






Penny Wise Pound Foolish

Penny Wise Pound Foolish

For those of you who aren’t familiar with this expression, it means to be cautious (wise) with small amounts of money but wasteful (foolish) with larger amounts of money. Does it happen often? Absolutely! I have no doubt it happens in every field and speciality, but it still amazes me just how often it happens in the IT industry.

The catalyst to this post was the recent Sophisticated Cooling Apparatus post on The Daily WTF. The picture alone says a thousand words! As you see from the picture, you have a hardware setup worth thousands, tens of thousands. Not only that, but those machines had to be configured and setup which costs time and money. And I suspect they’re also running a lot of expensive custom software.

What’s truly amazing is that the biggest failure point is a $5 used fan that’s attached with a note to not remove or unplug it because the whole system will collapse. Huh?!? A massive system with large resources (not just hardware and software, but also people) is at the mercy of a basic cheap fan! It makes no sense at all.

Which lead me to search for other examples, and it didn’t take me long at all to find many other examples. It just so happens that The Daily WTF had another article rightly entitled Penny-Wise, Pound-Foolish. And that story was even more appropriate!

In it, a bank hires a $300/hour contractor to setup monitoring software to analyze traffic on $5,000,000 worth of servers. Obviously you’d think they want to get their money’s worth from the contractor, especially at that hourly rate. You’d definitely want to give him a great computer system to work with. You’d want to completely pave the way so that he can work as efficiently as possible. Or so you would think…

Unfortunately that’s not what happened. The person got assigned a completely under-performing system that could barely run Microsoft Office! Never mind actually writing any code. So here you have someone who’s at the mercy of an obsolete computer that can barely even function. What a waste of time! Considering that a newer machine could be had for under $2000, that’s less than the consultant’s daily cost. And remember, in this scenario most of his time is sitting there waiting for the computer to just respond to a command (it had 256MB of ram – not even enough to run Windows XP).

The consultant of course complained, asking for a more powerful box. He was of course denied. He got the all too common response of “let’s make due with what we have for now”. And he’s not the only one to experience this. I’ve known many developers who’ve brought in their own hardware (including myself). This is hardware they paid for themselves! It happens too often.

And by the way the above story is even worse than my summary, but I think you get the point.

So why does it happen? And especially why does it happen so often? Because in larger companies and governments it’s all about budgets. More specifically whose budgets. For example the cost of the upgraded computer for our lowly consultant would probably be coming from another budget, one where they didn’t want to spend their money on something that didn’t give them a direct ROI. There’s no bigger picture here. It’s not like at a small startup where every penny is highly valued. It doesn’t make sense, but unfortunately that’s how the game is played.

Another possibility is the process to acquire the upgraded computer. Often in big bureaucracies it’s easier to acquire new people, even very highly paid consultants, than it is to acquire simple and cheap hardware. I’ve personally seen it many times, and I’ve heard about it even more. I remember at one time spending almost the same amount of money debating the value of getting more advanced hardware as the hardware itself cost. And it was no ones fault, that’s just how it works.

And that’s why you end up with a contractor basically being paid to sit for $300/hour. The larger bureaucracies can absorb this cost because of their size.

However all is not lost. I have a simple solution to propose. Let’s assume that not everyone is an idiot required to fill in ID-10-T forms for every little request. Let’s assume people for the most part are good and want to do a good job. Let’s TRUST people.

Instead of requiring a large process to get a computer upgrade, purchase a smaller software application, etc., let’s assume they know what they’re doing and let them do it. Give them a discretionary budget to spend on things that will make their jobs more efficient. Let them maximize their performance.

Sure some people will abuse the system, but that’s nothing compared to the state we’re in now. Using the example of the $300/hour consultant, it would take a lot, and I mean a LOT of abuse to outweigh the benefits this type of trust system would give you. Even if the consultant decided to purchase a $10k computer, the company would still be ahead!

But it gets better. Remember that for each request sent out, it has to get the approval of several people. Even that $50 stick of ram needs to be approved by several people. And a process had to be put into place. In other words, getting the approval to purchase $50 of ram probably costs a LOT MORE than the $50 stick of ram. I’d bet it easily costs over $200.

Which means that if you compare the trust system to the current system, you could technically have 4 people completely abusing the system for every real request and still come out ahead! And I don’t believe for even an instance that 400% of the requests are from people trying to abuse the system.

But wait, it doesn’t end there, you also get another great benefit. If you trust people they will be more productive. Not only are you saving money, you’ll get better results. People who are trusted are more motivated. When you have a good team that gels well together, they can do great things. If you embody distrust and bureaucracy, well things come to a crawl and any and all motivation slowly dwindles to nothingness.

Trust your people. The results might surprise you.






Why Have a Start Button to Shutdown Windows?

Windows Start Button

The other day I was watching David Pogue’s presentation When it comes to tech, simplicity sells on Ted Talks about good and bad UI design, which overall was very good. However good UI (User Interface) design is not always as obvious as he makes it out to be, and sometimes it’s even very counter-intuitive.

For example, it’s very easy to bash Microsoft Windows. Not that I’m a “fanboy” of any particular operating system, at LandlordMax we work with Microsoft Windows, Mac OS, and Linux, so I’m pretty operating system agnostic. In any case, David presented some good examples of good and bad UI design, and in most cases he was 100% right. In one comparison, for example, he compared the difference between the shutdown menus on Windows and the Mac OS. The Mac OS definitely had a more obvious and easier user interface.

What got my attention however, and the reason I’m writing today, is that he also made a passing joke about Windows and how you shut it down. He said “Why in gods name do you shutdown a Windows PC by clicking on a button called Start” (approx 11 minutes into the presentation) which drew some laughter from the audience. I agree his comment makes sense intuitively, but he’s unfortunately wrong in this case.

The Old New Thing

Raymond Chen writes about Microsoft’s decision to use the “Start” button in his book The Old New Thing. It’s too bad David hasn’t read Raymond’s book or blog post about the decision to use the Start button. Basically it goes that in the beginning of Windows there was no “Start” button, they only added it after going through some usability testing.

To quote Chen (the highlights are my own):

Back in the early days, the taskbar didn’t have a Start button. (In a future history column, you’ll learn that back in the early days, the taskbar wasn’t called the taskbar.)

Instead of the Start button, there were three buttons in the lower left corner. One was the “System” button (icon: the Windows flag), one was the “Find” button (icon: an eyeball), and the third was the “Help” button (icon: a question mark). “Find” and “Help” are self-explanatory. The “System” button gave you this menu:

Run…
Task List…
Arrange Desktop Icons
Arrange Windows 4
Shutdown Windows

(“Arrange Windows” gave you options like “Cascade”, “Tile Horizontally”, that sort of thing.)

Of course, over time, the “Find” and “Help” buttons eventually joined the “System” button menu and the System button menu itself gradually turned into the Windows 95 Start menu.

But one thing kept getting kicked up by usability tests: People booted up the computer and just sat there, unsure what to do next.

That’s when we decided to label the System button “Start”.

It says, “You dummy. Click here.” And it sent our usability numbers through the roof, because all of a sudden, people knew what to click when they wanted to do something.

So why is “Shut down” on the Start menu?

When we asked people to shut down their computers, they clicked the Start button.

Because, after all, when you want to shut down, you have to start somewhere.

(Besides, if we also had a “Shut down” button next to the Start button, everybody would be demanding that we get rid of it to save valuable screen real estate.)”

The morale of the story, be careful before you start trashing user interfaces, there might just be a reason for some odd solutions.


TripLog/1040

And this was proved again recently in the post Learning from “bad” UI on Signal Versus Noise when Ryan walked us through the UI design of TripLog/1040. This UI at first seems to be horribly designed, with no thought at all given to it. But once you understand the reasoning and usability behind it, you quickly realize that it was indeed very well designed and that a LOT of thought was actually given to the design of its UI!

Of course many UI’s are just badly designed, there’s no question about it. It’s just that sometimes the obvious is not so obvious!






How To Quickly Setup a Software Development Environment

Setup New Developer Computer Environment

At one time or another all software developers have joined a project or team where the development was already underway. The most common introduction for a new developer to these projects is to setup their development environment. If you’re extremely lucky, a lot of this work is already done within the corporate disk image, but more often than not it’s up to you. This is especially true if you’re a contractor.

For a typical Java project, the story goes something like this. Download and install a specific JDK (Java Development Kit). Download and install a particular IDE like Eclipse. Download and install a specific web server. By the way, depending on the company, some of these versions may no longer be available which means you have to track down someone who still has an available copy somewhere. But assuming they’re all the latest versions, that’s just the start.

Once that’s done, you have the core applications to setup. Then you get to start downloading and installing the support applications. If you’re using Eclipse, maybe it’s the SVN (Subversion) subclipse plugin or the TortoiseSVN client. A good text editor such as UltraEdit. FireFox. The list goes on for about a dozen or so applications.

After that you probably need to configure your system settings. Most likely you then have to set up some development environment variables or configurations (server.xml, eclipse.ini, etc.). Save some connection settings in Putty. Setup some network drive connections. And on it goes.

But the fun doesn’t stop there! At this point you’ve just got your environment setup, you haven’t downloaded the project’s source code. Now you need to checkout the project from the source. Once that’s done you need to run the automated build system to get the project up and ready on your box, perhaps using Ant or Maven. If you’re lucky, and the company was in the upper technological echelon it ends there. Unfortunately most companies don’t completely automated their builds so you often still need to do some extra manual tweaking to get your project running. And on it continues.

Which means that all new developers lose anywhere from a few hours to several days setting up and configuring their development environments. Very expensive! And the longer your project goes (there will be some developer turnover with time) and the bigger your team gets, the more expensive it gets. Plus, with time this knowledge will disappear. People will forget steps, or why things are done in a certain way. Which means that at some point setting up a new project will be, well, for lack of better word, insanely difficult. You’ll have to track down specific people within the project because they’re the only ones left who know anything about certain parts of the system. If you’re really unlucky, the only way to get up and running might be to you copy the complete environment from one computer to the other. Yes complete copies of directories! It can become a mess.

The good news is that there’s a much much better alternative. One that will make everyone’s life much easier, and keep the knowledge over time. Can it really be? Yes, we don’t all have to suffer through this. And what’s even better is that I’ve implemented what I’m about to suggest with great success several times, and not just at LandlordMax but also at companies I’ve consulted for in the past.

Create a developer’s installer!

Why should your final product be the only system with a deployment strategy? Why not create one for your developer environment as well. Especially if your company doesn’t have a fully pre-prepared disk image ready with everything (which is virtually guaranteed).

How hard is this to do? It’s actually extremely simple, simpler than you might think. If you’re company already owns an installer like Install4J (which I’ve recommend in the past), then it’s a matter of an hour of someone’s time. And it shouldn’t just be kept on someone’s machine, it should be it’s own full project within your version control system. This way if any developer does any environment change, it’s up to them to update the developer installer project. This includes if an application needs to get updated (for example to the latest version of Eclipse, the latest version of FireFox, etc.). This isn’t just limited to new software updated, but also configuration changes, etc. No longer do you need to keep a record of configuration change emails, it’s all done for you in the developer installer!

Therefore when a new developer starts, all you have to do is get the latest executable build, copy it on a USB key (network drive, whatever), and let them run it. Better yet, you can do it the day before they come if they’re going to use in-house hardware. During this time you can walk them around and introduce them to the rest of the people they’ll be working with, explain them the project in more detail, or any other higher value action items you may have.

And don’t worry if your company hasn’t bought a great installer building tool like Install4J, you can also use the open source installer creation system called NSIS. Up until LandlordMax, I generally relied on NSIS to create Developer Installers since I could never guarantee what was available in the different development environments, but with LandlordMax we use Install4J. Both are great, the main difference is that with NSIS it will take you a lot longer and you’ll need to acquire a lot of specific NSIS knowledge (it’s a scripting language). So instead of an hour or so for Install4J, expect it to take a few days to a week or more with NSIS to build a good installer. But even if it takes a week, it’s worth it!!! The good news is that it doesn’t have to be production quality, just beta quality. In other words, you don’t have to include all the checks like in a production installer, you can assume that your users are pretty smart and will mostly enter in valid values (for example you can assume there’s enough disk space on the hard disk, that it exists, that’s writable, and so on).

The good news is that an NSIS installer script shouldn’t be more than a few dozen lines to a couple hundred lines (assuming you’re using functions and macros available on their website). So that’s not too bad overall. With Install4J it’s all GUI based, and the resulting script is stored in an xml file which can be called from any Ant build script! The cost to benefit ratio for one person to translate all the developer environment knowledge into an automated installer for everyone that will be available forever is unbeatable!!

A few quick tips:

  • Don’t re-create your main build file (Ant or Maven in Java), leverage what’s already there. In other words, you developer installer can call the main build file in your project to complete the environment setuo.
  • Use silent installers. Most applications have silent install options, so use them. The less interaction your developer installer requires the better.
  • Give the developer the option of selecting the initial install location.
  • As part of the install, leave all the individual installers within the main install directory (for example leave the FireFox setup executable somewhere in the install path so that it can be run separately later).
  • Give the option to select which “modules” to install. For example give the option to install FireFox, to install Eclipse, to configure the server.xml configuration file, etc. I’ve done this in the past by presenting a list of options (checkboxes) the user could select. By default all are selected with a toggle to unselect all of them near the bottom.
  • Assume advanced users are using your installer. In other words, you don’t need to spend too much time on checking for user errors. Let it fail miserably if there’s an error. It’s not worth the time.

As an added benefits, everyone has the same development environment initially by default. With time some developers will deviate from the standard development environment to what they like, but it’s good to start the same way. At least this way you have a default method to setup a developer environment which is a million times better than having a bunch of different developers setting up your environment in slightly different and unique ways. Plus if there’s an issue, it’s REPRODUCIBLE!






How to Write Maintainable Code

The Core: Finch

Read Code Complete 2. Read Refactoring. Read Design Patterns. Read The Pragramatic Programmer. Read Concurrent Programming. And any number of other great software programming books.

But more than that, more than any book can teach you, write your code from the perspective of the person who’s going to use it. It’s as simple as that:

Write your code from the perspective of
the person who’s going to use it.

Yes that simple, but realize that it comes with very large ramifications. It means that you can’t write your code for what’s simplest for you from your current view/layer, it means you have to write what’s simplest for the person who’s going to call your code. You really have to think of how your code will be used from their layer. How would they want to write their code. What’s the least amount of information they’ll need to know.

For example, within LandlordMax we have a report generating framework. Every report passes through this framework because we’re using the DRY principle (Don’t Repeat Yourself). But more than that, if we can we abstract it out so that all the next developer has to think about is how to get the data from the report in the correct object. They don’t need to know how the report screen opens up, how the xml report template is loaded, how the logo/letterhead is loaded onto the report. It’s not important.

When I initially developed this section of the code, I took the stance that the effort on building and maintaining this framework would be a lot less than the cumulative time spent generating reports (we already have over 100 different reports). Therefore I decided to spend more time up front to do it well so that future developers would be able to quickly and efficiently get up to speed. And if it needed documenting, then it was probably too difficult. Need being the keyword! It doesn’t mean I won’t write any documentation, it’s just that it should NOT be needed.

So from here to the end of this post, I’ll walk you through my thought process on how I went ahead and built the reporting framework from the other person’s perspective. I believe that this is the best way to describe what I’m trying to explain. Just saying develop from the other developer’s perspective is easy, but what does that really mean. And by walking through the logical steps I took I think what I’m trying to explain will become much more evident.

But before I begin, let me start by saying this example assumes an OO (Object Oriented) programming language and some knowledge of OO (Object Oriented) design. As well, please note that the application I’m going to use here is a real implementation in a real application. It’s the reporting framework within our product LandlordMax. And because of this, the example is more desktop centric (we only offer a desktop application but this will eventually change).

As I mentioned before, on requirement was that it should be easy for developers to jump in because there are going to be a lot of reports. Therefore our main goal in this example is to create a report printing framework that will be easy for developers to use.

Because the software is a desktop application, and it uses an OO language, the first thing we should do is create a generic panel for our report screens. By this, we’ll divide it up into three sections: one section to select which report to generate, another to enter in data for the report (for example a date range), and one last section to display the report results. Sure we could have put the results on another panel, but for now let’s just go with this UI decision.

Assuming this, the first thing we need to do is create a parent/super class for the report panel, which we’ll call SingleReportPanel. And any report we create from then on will extend SingleReportPanel. We’ll take for granted that there’s some code somewhere else to manage all the report panels, which means the developer doesn’t need to worry about this. Or at the very least nothing more than registering the report panels so that the framework knows they exist.

So what does our SingleReportPanel need to do? Well most likely it needs to generate a title to the report (so that we can select it from a list of reports). It needs to be able to generate a panel to get more data for the report (such as a date range, a tenant from a list of tenants, etc.). And lastly our report needs to be able to display the results. At least for now. So our current implementation of SingleReportPanel should be:

class abstract SingleReportPanel
{
    public abstract String getReportTitle();
    public abstract JPanel getDataJPanel();
    public abstract ... generateReport();
}

Notice in this example I don’t show any code that needs to be implemented by the SingleReportPanel. Not even the private internal methods! We’re implementing this from the other developer’s perspective, We don’t care at this point. We don’t want to see it. It’s unimportant. The less they need to know the better. At this point all of the objects the developer needs to return to the framework are obvious (at least those that we’ve defined to this point – we’ll discuss the generateReport shortly). Not only that, but because we use a JPanel as a return object, the other developer can pretty much write any UI code whatsoever and it will work. It’s not at all dependent on having any framework specific knowledge. All UI code works!

Regarding the generateReport function, what type of return object do you think we should define? In our case, all reports are going to be table based (here’s an example of a generated report). But we also want the option to extend this further in the future.

At this point a lot of developers would start looking into custom structures, custom objects, and so on. Things ranging from HashMaps to ArrayLists of ArrayLists. Perhaps creating a custom Object with attributes and what have you. You name it. Basically specialized structures. However that’s not necessarily what you want to do. In our case, we know we wanted to return a table structure with flexibility. Is there something within the language that already offers that which is standard? With Java you can use TableModel. This is an interface to table data. It’s not specific to Swing, it can be used in a web application as much as a desktop application. It has all the structures we need, including the ability to name columns, store data, sort the data, and so on. But best of all it’s not custom, it’s a standard library object.

Hackers Movie

And being a standard library object gives you a lot of advantages. Firstly the learning curve is negliable. You have to assume that all developers know or understand anything within the core language library. What’s more, the potential to leverage open source tools and components goes up exponentially when you use core language libraries! So right off the start you get some great benefits. And don’t forget that as the language is updated, the API’s can become more and more powerful.

As a quick side note, if you were thinking why not just send the database results data back directly (ResultSet) you’d be wrong for several reasons. Firstly, you’d be sending the responsibility of managing the database connection up and down the layers. You’d also be creating a lot of coupling between the database layer and your presentation layer. You’d be forcing database schema knowledge onto the different layers of your code. You’d also be locking your implementation to a database (what if you decided one day to use webservices, etc.). In other words you’d be breaking almost every OO rule out there.

What about just passing the Data Model Objects back as an array? You’d have a similar problem. What if the report crossed over multiple data model objects such as a tenant and a building. But ignoring that, it means that your reporting framework would need specific rendering information on how to properly display the information.

Therefore the best method is to pass back a TableModel. Every developer who’s ever worked in Java should know what a TableModel is. It’s used everywhere from desktop to web applications. In Swing you populate your JTable‘s with TableModels. Even the MyFaces implementation of JSF uses a TableModel for the table widget for the very same reason. Not only that, but the TableModel has the ability to pass within it renderers so that your data can look pretty. This is standard functionality within the core libraries.

Knowing all this, if you were to become a new developer at LandlordMax do you think it would be difficult to create a new report? Probably not. All you’d need to do is create a new class file, then extend it from SingleReportPanel. This would immediately cause a compiler warning, where you the IDE would offer to create the three abstract methods we created above. You’d then be responsible for filling in code such as illustrated below:

public class HowEasyIsThisCodeToImplementReportPanel extends SingleReportPanel
{
    public String getReportTitle()
    {
         return "How Easy is this Code to Refactor";
    }

    public JPanel getDataJPanel()
    {
         // Standard Swing code.
    }

    public TableModel generateReport()
    {
        // get data from dataJPanel
        // call database with data from dataJPanel
        // add any logic at all (other db calls, massage of data, etc.)
        // return standard TableModel
    }
}

Please note that the generateReport() should never directly talk to the database otherwise you’d be coupling your database to the report framework. Rather you should go through another similar layer to communicate with the data storage mechanism. By doing this, you can call a database directly with SQL, you can use an ORM framework such as Hibernate, or you can even communicate with multiple different data storage systems! And for those who don’t like too much abstraction, you’ll notice that it’s very minimal. It’s not layers upon layers of abstraction, the key mechanism here to make this happen is just where you place your code.

Office Space Movie

I’ve of course omitted a few details, such as how to get the report template file (an XML file) from the system, but this can just as easily be abstracted out as well. Assuming a naming and location convention, you could just create an abstract method such as (I put the return object as a String but this could be easily refactored later):

public abstract String getReportTemplate();

which would return:

public String getReportTemplate()
{
    return "HowEasyIsThisCodeToRefactorReport.xml";
}

The real beauty of this whole design is that it’s fully extensible. You’re not limited by any shortcomings of your thinking when building the framework. It has full the capabilities of being extended beyond anything you can imagine (for examples the data panel could potentially include another complete application!!!). You’re only limited by your imagination.

But most important of all, you don’t need to know anything about the framework!!! The code is simple and easy to read. It uses only standard conventions and libraries. A new person can get up to speed within minutes. Does this code even need to be documented? Any decent programmer can see the SingleReportPanel abstract class and quickly see what needs to be done. Look again at the class:

class abstract SingleReportPanel
{
    public abstract String getReportTitle();
    public abstract JPanel getDataJPanel();
    public abstract TableModel generateReport();
}

Do you need to know anything about the internals of the reporting framework. Is it limiting you withing any constraints of the framework? Do you need to know about any custom reporting framework structures, objects, etc.? Does it allow you to extend it with virtually any other system? The simpler you can make the implementation from the other person’s perspective the better!

Did you notice one other thing? I never once talked about how to implement the framework from our perspective. Maybe working with a TableModel isn’t the best way of working with our reporting framework. Maybe we’ll have to convert this object to a custom reporting framework object. The truth is it doesn’t matter from the other developer’s perspective. Nobody else should have to learn the reporting framework other than you! Why would you want to burden EVERYONE with this knowledge. Do they benefit from it? Unless you hit a performance bottleneck, use standard constructs to pass information back and forth. It’s much much easier for all future developers. And rest assured more time will be spent developing reports than building the reporting framework over the lifetime of the software.

But wait, above that you get a free set of Ginzu knives! But seriously, there’s more. What happens if you change the reporting framework later? Right now at LandlordMax we use JFreeReport. What would happen if we decided to move to Jasper Reports, Crystal Reports, or any other reporting framework? If we didn’t abstract this knowledge and instead forced every single developer to map to the reporting framework’s object/structure, then all our reporting code would need to be re-written if they used another object/structure. And that for every single report! Every developer would then also have to learn this new framework. What a waste of time, there’s no business value to it. You’d never change reporting frameworks no matter how much better the other one might be, the costs would be way too high. And how boring would it be? Do you want to do this for over 100 reports? What about over 200 reports? 1000 reports? The cost goes up and up with time as you add more and more reports to your program.

By using a standard library object, if the reporting component was ever to change all you’d need to do is change how SingleReportPanel uses the generateReport() method. No one, and I repeat, no one even needs to know you changed frameworks!

Which brings us back to the original premise of this article, the most important thing you can practice to write maintainable code is to write code from the other’s developer’s perspective. Look at how they will use your library. What’s the best and most productive way for them to use your code? And remember the other developer doesn’t need to be another person, it can just be you at another abstraction layer.

PS: I’ll give away a free copy of my ebook How to Generate Traffic to Your Website to the first person who can correctly name the three movies the pictures on this post come from.






Why You Can't Just Compare Languages With Lines of Code

Comparing Apples and Oranges

It’s incredible the number of bloggers comparing one computer programming language to another by just comparing a small snippet of code and saying “See look, I can write this in 5 lines of code where in this other language it takes me 50″. This is especially common for comparisons between languages such as Java versus Ruby (specifically Ruby on Rails versus any J2EE framework).

I hate to be another person adding fuel to this same fire, but I have to speak out. These comparisons just don’t work. It’s not even comparing apples to oranges, it’s comparing apples to liquid nitrogen. There is just no comparison, they’re completely different.

The comparisons are comparing the built-in libraries and not the languages.

The big difference with languages like PHP and Ruby is that a lot of functionality is built into the native libraries whereas Java has more verbose API’s (and it’s been built to handle more than just web applications). But that’s ok, that’s what open source is for. Maybe you can’t just do something like image.rotate(90) in Java with the native APIs, but nothing prevents you from getting an open source image library that does. There are tons that do. And after you’ve done it once, you’ll always know how to do it. The comparison no longer works.

Again, this is a comparison of the built-in library API’s. Java’s is much more detailed and lower level to allow you more flexibility whereas the others offer the high level functionality for free. Which is better? It depends. In either case, I can take that 50 line implementation in basic Java and extract it to a few lines by using open source libraries. So that’s not a valid comparison. And if you really wanted to push that, then you should also compare what the open source image library offers in terms of image manipulation compared to the other built in language! Again you can’t compare, it just doesn’t work.

The issue with most of the comparison examples is that are too small. A 50 line code snippet doesn’t mean anything. We all talk about how university/college computer science courses don’t really prepare you for the “real world”. Another case in point. A few lines of code doesn’t not represent a real world application.

LandlordMax I built it expecting lots of versions and different people working on the code base. How important is it to get to market first? Personally I don’t think it’s that important. And David from 37Signals really iterates it well in this video, being first is not always a good thing. My company LandlordMax was not the first by far and yet we’re succeeding very well.

Therefore you need to determine where you want to be in a few years. Speed of development is important but it’s not the end all be all. If it is, realize that your growth cost in the following graph will increase exponentially faster. Twitter is being hit by this right now. They burst right out of the gates but right now they’re having problems with that growth. PayPal is also falling apart. I have no doubt that the cost to add new features to PayPal is excruciatingly expensive. We’ve all been part of projects or systems where adding anything, and I mean even the simplest of changes, required extreme efforts. Some systems become unmaintainable.

To understand just how bad code can get, I recommend viewing Alberto Savoia’s presentation at BOS last year. That or visiting TheDailyWTF almost any day.

Scalability (Team Size – NOT Performance)

Everyone seems to talk about scalability in terms of performance, but what about in terms of team growth? If you only have a few developers you can use any language successfully. But as your team size grows some languages don’t scale so well anymore. Ever try to work on a PHP system with a dozen developers? Unless you have, you can’t really appreciate how difficult this can be. With Java, it’s still difficult, but not nearly as much. And it’s not just the tools, certain language lends themselves better to having multiple developers. For example with a dynamic language there are some theoretical limitations on what you can possibly know at compile time within the program. With statically typed languages, there are still limitations, but they’re a lot less. If you don’t know what I’m talking about right here, don’t worry, it’s pretty technical and I’ll discuss this in depth in another post.

I can already hear arguments such as “with Ruby on Rails you’ll never need 10 developers”. I doubt that, but ok, let’s go with that argument. Will they always be the same 10 developers? Not likely. In which case will the replacement developers be able to pick up where the others left off? With dynamically typed languages this is much harder. The IDE’s, because of the theoretical limits, can’t offer the same level of tools and support. For example, because you can’t know for sure of a variable is of a certain type at compile time you can’t provide a tool to drill down into the object within the IDE. This can only be done at runtime.

Another big aspect of dynamic versus static is that it’s very difficult to create refactoring tools in dynamic languages. Again, for the same reasons, because we can’t know the type an object until runtime, most refactorings need to be done by hand with “find/replace string”. This is difficult and very error prone, which is why it’s not often done. And the more refactorings are skipped, the worse they will get with time. Exponentially worse!

Scalability (Code Size)

Another big argument used in comparing programming languages is that one language will take up a fraction of the code base as another. We’ve already alluded to this with the library discussion, where this is not necessarily true (actually I believe it’s not at all true). But in any case, let’s assume it is. Does the language scale up to bigger code bases?

For simple applications it doesn’t matter too much. For example the business logic behind our Free Real Estate Analyzer is pretty minimal. I won’t say there isn’t any, but if you compare it to a normal application for a business (or god forbid an enterprise web application, or even a government application) the business logic is extremely simple. No exception cases, no weird possibilities, no crazy logic. How do these languages scale up for all these cases?

Scalability (Performance)

And of course you have to talk about performance. How do they scale up in terms of actual performance? But notice I put this near the end!!! I don’t think this should be a central theme because all things being equal, the differences aren’t going to make or break the decision unless you expect millions of visitors on a daily basis. And even then you have money for more hardware and smart people to figure out the bottlenecks.

Miscellaneous

What about integrations with other systems? If you’re a contractor and need to work in larger companies you’ll find that most new systems have to integrate with older existing systems. How well does you’re language work with this. Even small businesses have to do this!

What about deployment? If you’re going to be selling it as an application that your customers need to install, can it be easily deployed? For example RoR has some issues with this. Even PHP can be a beast to deploy (think shared hosting). Java also has it’s issues, but if you do it well all you need is a ear/war file.

What about combining the system with other components? For example PHP is problematic if you want to include several open source components because of namespacing. This isn’t necessarily bad, it’s just something you need to be aware of.

What about extensions of the language? For example Java has more open source libraries than any other language. That’s an amazingly powerful asset. You need something that’s not in the language, go find an open source library. And usually there’s more than one great option. RoR is still lacking in this respect, but that’s because it’s still relatively new. I suspect this will change a lot with time.

Conclusion

I guess what I’m trying to say in a very round about way is that you can’t just compare languages directly based on how many lines of code it takes to implement a small task. That’s the worse comparison of all. What you need to do is look at what you’re trying to do as a whole today and tomorrow and base your decision on the factors you find important. Different languages are better for different scenarios.

For example if you need high initial productivity than by all means use a language like PHP or RoR. If you’re only building a one-off application that’s simple and will never grow, use PHP or RoR. But if you’re planning to build a system that will be around for a long time with lots of people passing through it, then use Java. Anywhere in between, well that’s the gray area where either type of language could easily work. It all depends on where you want to go and what you expect the lifespan of your system will be.

Something else I’d like to note, I’m seeing a lot of people getting excited by MVC frameworks in the dynamically typed languages. This is great to see because before there wasn’t really any consistent framework, but it’s still years behind what’s going on in the Java world. MVC frameworks were a great jump forward, and thank you Struts for bringing that mainstream (about 8-9 years ago).

But even now the Java community is looking at alternatives to the MVC frameworks, it’s been found deficient. It’s a great step forward, but it’s not enough. Right now we’re starting to see event based web frameworks such as JSF and GWT really picking up steam. And I’ll be honest, once you’ve tried an event based framework it’s incredibly hard to go back to an MVC framework. It’s just so much more intuitive and powerful. I just hope the other languages start to look at creating new event based frameworks sooner than later. They’re very powerful!






 
FollowSteph RSS
FOLLOWSTEPH'S
RSS FEED!


SOFTWARE AND BOOKS BY STEPHANE GRENIER:

LandlordMax Property Management Software

LandlordMax is the EASIEST
Property Management
Software available!
Try it for free!

Real Estate Pigeon

Real Estate Pigeon
The place to ask and answer
all your real estate questions

Blog Blazers: 40 Top Bloggers Share Their Secrets to Creating a High-Profile, High-Traffic, and High-Profit Blog!

Blog Blazers is a book that
features secrets from the
Top 40 Bloggers on the web

How to Generate Traffic to Your Website ebook

How to Generate Traffic to
Your Website
is an ebook for
to you achieve success


 

FollowSteph
More resources from Stephane Grenier:
PUBLICATIONS
For people who work on the web
Blog Blazers
How to Generate Traffic to Your Website
 
SOFTWARE
The EASIEST Property Management Software available!
LandlordMax


Copyright @2003-2013
LandlordMax Property Management Software

Disclaimer: This is a personal blog about my thoughts, experiences and ideas. The contents of this blog are for informational purposes only. No content should be construed as financial, business, personal, or any other type of advice. Commenters, advertisers and linked sites are entirely responsible for their own content and do not represent the views of myself. All decisions involve risks and results are not guaranteed. Always do your own research, due diligence, and consult your own professional advisors before making any decision. This blog (including myself) assumes no liability with regard to results based on use of information from this blog. If this blog contains any errors, misrepresentations, or omissions, please contact me or leave a comment to have the content corrected.