You are currently browsing the category archive for the ‘Boring’ category.

.NET Framework 4.5 contains a very cool new feature called Multi-Core JIT. You can think of it as a profile-guided JIT prefetcher for application startup, and can read about it in a few places …

I’ve been using .NET 4.0 to develop Paint.NET 4.0 for the past few years. Now that .NET 4.5 is out, I’ve been upgrading Paint.NET to require it. However, due to a circumstance beyond my control at this moment, I can’t actually use anything in .NET 4.5 (see below for why). So Paint.NET is compiled for .NET 4.0 and can’t use .NET 4.5’s features at compile time, but as it turns out they are still there at runtime.

I decided to see if it was possible to use the ProfileOptimization class via reflection even if I compiled for .NET 4.0. The answer: yes! You may ask why you’d want to do this at all instead of biting the bullet and requiring .NET 4.5. Well, you may need to keep your project on .NET 4.0 in order to maintain maximum compatibility with your customers who aren’t yet ready (or willing Smile) to install .NET 4.5. Maybe you’d like to use the ProfileOptimization class in your next “dot release” (e.g. v1.0.1) as a free performance boost for those who’ve upgraded to .NET 4.5, but without displacing those who haven’t.

So, here’s the code, which I’ve verified as working just fine if you compile for .NET 4.0 but run with .NET 4.5 installed:

using System.Reflection;

Type systemRuntimeProfileOptimizationType = Type.GetType("System.Runtime.ProfileOptimization", false);
if (systemRuntimeProfileOptimizationType != null)
{
    MethodInfo setProfileRootMethod = systemRuntimeProfileOptimizationType.GetMethod("SetProfileRoot", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null);
    MethodInfo startProfileMethod = systemRuntimeProfileOptimizationType.GetMethod("StartProfile", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null);

    if (setProfileRootMethod != null && startProfileMethod != null)
    {
        try
        {
            // Figure out where to put the profile (go ahead and customize this for your application)
            // This code will end up using something like, C:\Users\UserName\AppData\Local\YourAppName\StartupProfile\
            string localSettingsDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            string localAppSettingsDir = Path.Combine(localSettingsDir, "YourAppName");
            string profileDir = Path.Combine(localAppSettingsDir, "ProfileOptimization");
            Directory.CreateDirectory(profileDir);

            setProfileRootMethod.Invoke(null, new object[] { profileDir });
            startProfileMethod.Invoke(null, new object[] { "Startup.profile" }); // don’t need to be too clever here
        }

        catch (Exception)
        {
            // discard errors. good faith effort only.
        }
    }
}

I’m not sure I’ll be using this in Paint.NET 4.0 since it uses NGEN already, but it’s nice to have this code snippet around.

So, why can’t I use .NET 4.5? Well, they removed support for Setup projects (*.vdproj) in Visual Studio 2012, and I don’t yet have the time or energy to convert Paint.NET’s MSI to be built using WiX. I’m not willing to push back Paint.NET 4.0 any further because of this. Instead, I will continue using Visual Studio 2010 and compiling for .NET 4.0 (or maybe I’ll find a better approach). However, at install time and application startup, it will check for and require .NET 4.5. The installer will get it installed if necessary. Also, there’s a serialization bug in .NET 4.0 which has dire consequences for images saved in the native .PDN file format, but it’s fixed in .NET 4.5 (and for .NET 4.0 apps if 4.5 just happens to be what’s installed).

Back in 2000, I had just finished my freshman year of college at Washington State University and was back at the best summer job you could possibly imagine: sales associate at the local OfficeMax (office supplies store). Now, by “best” I really mean “boring” but it wasn’t a bad job, especially for a 19 year old (at least I wasn’t flipping burgers, right?). Selling printer ink and paper wasn’t the best use of my time, but maybe there’s an analogy with Einstein working at the patent office (we can all dream).

Anyway, to pass the time I decided to see if I could write a fully professional-quality Windows utility from start to finish in just a few weeks. I had recently read an article by Mark Russinovich about the new Windows 2000 defragmentation APIs, and it had source code for a Contig command-line utility that would defragment 1 file. (Back then I remember there being some source code but I can’t find that now.) Using that information as a basis I decide to write my own disk defragmenter, because clearly a college sophomore knows how to do this stuff. It would have a GUI version (“Fraginator”) and a command-line version (unfrag.exe), full CHM documentation, and a mascot. I wrote it in some mangled form of Visual C++ 6.0 with STL, if I remember correctly.

“Defrag, baby!” The Fraginator was not joking around with your data (I was a bit goofier with my coding back then). The picture is actually stolen from Something Awful’s satirical Jeff K from one of his comics where he thinks he’s a buff super hero or something (warning: those links may not be safe for work, or even safe for life, but they are at least occasionally hilarious). Hopefully Lowtax won’t come after me for that.

I finished the project up, put it up with source code (GPL) on my podunk of a website (I think it was hosted at zipcon.net, a small Seattle ISP), and mostly just forgot about it…

… until last week when I searched around for it and discovered that the ReactOS guys had started to include it in the applications distro about, oh, 6 years ago. They had even converted it to Unicode and translated it to a dozen languages or so. I thought, hey that’s great, someone actually likes it and is maybe even using it! I certainly was not expecting to find anything of the sort.

I was browsing through their “new” source code tree for it and immediately found a relic in the dialog resource file, clearly from a goofier moment:

I think I had this label control in there for layout purposes only, as it clearly doesn’t show up in the UI when you boot it up. But wait, it gets better. They haven’t removed this (not a bad thing), and in fact they’ve translated it.

So there you go. “I am a monkey, here me eeK” in Norwegian (I think). If you scout around with a web search you should be able to find a bunch of other translations. The French one is probably the most romantic pick-up line you’ll ever find, and there’s no need to thank me for your next success at the bars.

The last timestamp on the Fraginator.exe sitting on my hard drive is from 2003 and it doesn’t seem to work on Windows 7 anymore, unless you use it on a FAT32 USB stick. I doubt it’ll even compile in Visual Studio 2010. Oh well Smile I’m glad the ReactOS guys are having use and fun with it. If you want the source, you’re better off checking out their copy of it. I don’t know if I even have a ZIP of that lying around anymore, and they’ve made decent improvements since then anyway.

Over the years I’ve had to put up with several individuals, and companies, trying to plagiarize Paint.NET by recompiling the program under a different product name and with their own name stamped into the credits. Sometimes they charge money for it. I even came up with my own term for it: “backspaceware.” Additionally, every once in awhile Paint.NET is spotted being sold on eBay.

And, as many of you know, Paint.NET used to be open source. Or rather, it was “released source” – the source code was released, but it was never an open and collaborative project that accepted unsolicited code submissions. I liked releasing the source code this because I felt there was value in allowing others to study it. About a year ago I decided I was tired of seeing plagiarized versions of Paint.NET and I yanked the source code from the website. However, the source code was still out there at various places on the Internet (hardly illegal). Even without the source code, a clever and skilled person could probably still decompile, modify, and recompile the program to say or do whatever they wanted it to.

The biggest problem was that, even though these were clearly unethical and deplorable actions, the MIT License permitted all of it. Or, at least, it was unclear in some corner cases what was disallowed. So, legally speaking, it wasn’t clear what exactly could be done about it anyway. I am not a lawyer and did not want to spend thousands of dollars to get it all figured out. Some people have stated that I chose the wrong license, and in hindsight this is definitely partially true.

Also, this is not just about plagiarism and my own personal blood pressure. Having derivative copies of Paint.NET out there causes confusion and disrupts the mainline user base. I’ve had people e-mail me confused because they thought that Paint.NET had been renamed, but that features were missing in “the new version”. These derivative copies also cause a bit of a mess, because they often 1) uninstall the real Paint.NET (they use the same Windows Installer product GUID), and 2) still have the same updater logic (including the URL for the manifest). Which means you’d install the derivative copy, it would remove Paint.NET, and then once Paint.NET had a new update it would uninstall the derivative version and replace it with Paint.NET, etc. Or, the modified version would crash and the crash log would still instruct people to send it to my e-mail address. There is also a real risk of trojans and viruses.

All that stops now.

For the final release of Paint.NET v3.5, which will be very soon now, I am updating the license. For most users, this will have no impact whatsoever. It’s still freeware. There’s still no claim on any files created, opened, or saved with Paint.NET. You can still mirror the ZIP file on your website (e.g. Betanews, download.com, etc.) without having to ask permission. You can still sell stuff that you make with Paint.NET (assuming you have the legal right to do so in the first place, of course). You can continue using it in a business environment, deployed to as many systems as you like.

However, the license now states that you cannot modify Paint.NET itself, or create derivative works based on the Paint.NET software (that is, derivative software). Nor can you sell it. I don’t believe this will have an impact for anybody but those who wish to plagiarize or rip-off Paint.NET. I’m not putting in any restriction about reverse engineering or decompiling, e.g. with Reflector. I think that would be silly, and I still whole heartedly believe that there’s value in being able to study Paint.NET’s code – even if it’s Reflector’s best-guess disassembly. However, you cannot modify and then recompile a new version of Paint.NET from that disassembly.

There will undoubtedly be some confusion here. For instance, “Are plugins allowed?” Absolutely yes – the program is designed to accept these, and they are not modifications to Paint.NET itself. No doubt I will have to update the FAQ for this, among other things.

I expect there will be a very vocal minority that will condemn this license change. Before you speak out, please ask yourself this question: Does it actually affect you? Were you actually planning to do something that this new license disallows? My guess is that the answer is “no”, but please post a comment if the answer is a legitimate yes. Many people had condemned my decision to remove the source code, but upon further investigation it was purely a matter of principle: they had never downloaded the source code, never knew anyone who had done so, and never planned to do anything that would benefit from or depend on source code access. I’d liken it to being upset that your passport disallowed traveling to Antarctica … were you really planning to do that in the first place?*

The other thing I am planning to do is to release portions of Paint.NET v3.5’s source code, probably under an MIT or BSD-style license. Plugin developers will greatly benefit from having the source code for the effects, and for some WinForms UI controls. The best way to summarize things is that this new license (below) covers “the binaries”, aka “what you just downloaded and installed.” I can still create separate download packages that are covered under different licensing terms. Philosophically it can be confusing, but I’m willing to pay that price.

Here is the new license, for your perusal before the imminent release of version 3.5:

Paint.NET

Copyright (C) dotPDN LLC and Rick Brewster. Portions Copyright (C) Chris Crosetto, Tom Jackson, Michael Kelsey, Brandon Ortiz, Craig Taylor, Chris Trevino, and Luke Walker.

Portions Copyright (C) Microsoft Corporation. All Rights Reserved.

Paint.NET is a registered trademark of dotPDN LLC.

License last updated: November 5, 2009

Paint.NET is free for use in any environment, including but not necessarily limited to: personal, academic, commercial, government, business, non-profit, and for-profit. “Free” in the preceding sentence means that there is no cost or charge associated with the installation and use of Paint.NET. Donations are always appreciated, of course! http://www.getpaint.net/donate.html

Permission is hereby granted, free of charge, to any person obtaining a copy of this software (the “Software”), to use the Software without restriction, including the rights to use, copy, publish, and distribute the Software, and to permit persons to whom the Software is furnished to do so.

You may not modify, adapt, rent, lease, loan, sell, or create derivative works based upon the Software or any part thereof. However, certain icons used in the Paint.NET user interface are from or adapted from those in the “Crystal” icon set, http://www.everaldo.com/crystal/, or the “Oxygen” icon set, http://www.oxygen-icons.org/. These icons are covered by the LGPL license, http://www.gnu.org/copyleft/lesser.html. These icons are stored as “loose” PNG image files in the Resources\en-US\ directory where Paint.NET is installed.

The above copyright notice and this permission notice shall be included in all copies of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

* Like all metaphors, this one has its limits.

As a computer scientist and software developer, I have been conditioned to live in fear, or at least gross distaste, of legacy. That is to say, legacy code, legacy systems, legacy process/procedures, etc. As a self-proclaimed cowboy rock star egotastic developer, anytime I see code that wasn’t written by myself (or a select group of those whom I deem as “peers”), my first instinct is to proclaim that it’s horribly written and that I should redo it. Likewise, the same goes for legacy systems and process/procedures. “Oh gosh, we’d be so much more efficient without all this process in place!” … “Oh gosh, our server is running on an OS that’s 4 years old, we need to upgrade it!” … “Oh gosh, oh gosh!”

Sound familiar? It should if you’re a software developer on a project with more people assigned to it than you can count on two fingers. I’ve found that one important part of career and professional growth in software development is learning how to deal with legacy, and to reign back the instincts to immediately and bombastically proclaim it all as a bunch of crap. Sometimes dealing with legacy means refactoring utility code while you’re fixing a bug that is dependent on it (“I’m in this part of the code anyway, why not?”). Other times that means checking out half the source code tree to make a tiny change in every single file that has positive consequences in areas of performance, stability, maintainability, or whatever. On rare occasions it really does mean rewriting all the code, or redefining the process by which you’re doing development.

Oftentimes, however, I’ve learned that you should just leave it alone.

If you have something in place that works, and you don’t have plans to change it, then guess what … it’s probably ok to leave it alone! Even if the code is nasty, or the implementation uses 5 year old design patterns, or if there’s a new intern who really really wants to prove him or herself. For example, I would argue that Notepad works. It does its job, it does it well, and there’s really no need to rewrite it to use C# or WPF or sock puppets or anything. It’s a stable legacy system. (That’s not to say there isn’t a need or a market for something more, such as Notepad++ or Notepad2.)

Now, you might wonder what in the world this has to do with Paint.NET’s bug database. Well, before I start, you should first answer the following question: What do you think I’m using for Paint.NET defect/bug tracking?

Ok, seriously, ask yourself that question. Give yourself at least 10 seconds to come up with a few different possibilities, both low- and high-tech. Pieces of paper stapled to the wall? Sourceforge? A custom WinForms or ASP.NET app? Notepad? Excel? The back of my hand and a marker?

Here’s the answer and the story:

Paint.NET’s bug database is Bugzilla 2.16.3 running on Apache in Redhat Linux 8.0. It was originally hosted on a Pentium II named “elbonian3″ in some dark corner of the EECS department of Washington State University. It was set up by (I think) Chris Crosetto during Fall semester of 2003 (~August) for the Computer Science 422 class (“Software Testing”) that Jack Hagemeister was running. The purpose was to inspect the source code of some client software that Gene Apperson (Microsoft alumni, Diligent founder) was developing, and to then file bugs on it. I was in this class and filed many bugs on the code, although I don’t know if Gene ever fixed the bugs or if his project was ever finished. Paint.NET started the following semester, and version 1.0 was written from start to finish in 15 weeks without any official bug tracking system. I think Brandon had an Excel file but nobody else really had access. I do not recommend doing that, by the way, that’s just the way things went down.

For the Fall 2004 semester, I talked with Jack and we set up a senior design project to ramp Paint.NET up to version 2.0. We got three students to work on it: Tom Jackson, Michael Kelsey, and Craig Taylor. The first thing we did was to have me finish up my work on version 1.1, and have the three students do bug filing and write updated documentation (haha, suckers!). As part of this, they managed to get the new semester’s CS422 students to do a lot of testing. To do this they picked up and ran with the “elbonian3″ server that had been set up the year before. It worked great: students would file bugs, I would get e-mails from the server, and we could respond to each bug appropriately. The three students learned a lot about bug triaging, as well as release and defect management. Next up, they did their development work to push Paint.NET up to version 2.0 which was released right before Christmas. Then Paint.NET was Slashdotted. It was amazing, and comedic: the attention caused enough network traffic to bring down the entire EECS network for 2 days.

For about the next year and a half, I continued to use this server remotely from 300 miles away to do Paint.NET’s defect tracking. However, in early- to mid-2006 it was clear that it was no longer appropriate to house this software at EECS. The network administrator was overworked (as they always are!) and the server was often down (for whatever reason), and Paint.NET was not really a “WSU project” anymore. The admin wanted to retire the server, and said he could send me the contents of the hard drive as a VMWare virtual hard drive file. I said that would be great, and the next day I started downloading a 2 GB tarball over HTTP.

Once I had downloaded and extracted the file, my next job was to install the free Windows version of VMWare Player on my server box in a dark corner of my house. This was no problem; it booted right up and spat out a login screen. But, you see, there was a major problem: the server still thought it was in Pullman on the eecs.wsu.edu domain. I managed to figure out what IP address the server had obtained, but whenever I tried to access it Apache would respond with headers pointing my browser back to the old eecs.wsu.edu address. Argh! Try as I might, I couldn’t figure out how to reconfigure Apache not to do this.

I’m sure some magical combination of grep and sed and other 3-to-4 letter combinations could fix it quick, but I found a faster-for-me solution: I cheated. I edited my hosts file in Windows so that it would map elbonian3’s network name to the local IP address of the virtual machine. Then, when I typed in the IP address in to my browser, it would get redirected to elbonian3 which would then get redirected right back to the IP address in a way that was completely transparent to the browser. Success! I had a working bug server again!

On a quick side note, about a month after I set this up I found that my IP range was being blacklisted by spamhaus or something. The reason was that sendmail was still active on my virtual server. Bugzilla kept trying to blast e-mail notifications about bug changes through an EECS mail server, which for obvious reasons rejected them. I’m a dumb clumsy oaf when it comes to Linux administration so I had to find a clever way to disable sendmail … I think in the end I may have just removed execute permissions on the appropriate binary. Bugzilla still claims to be sending out e-mails in its UI, but none of them actually get sent.

Anyway. A few weeks ago I decided to retire that server, as it wasn’t really doing much other than hosting this virtual machine and a CVSNT server. Why pay for that electricity if I can get it to run on my main workstation anyway? These were two things I figured could be done much faster if I just ran them locally (CVSNT is slow for Paint.NET’s repository on a 100mbit connection). There was another problem though: I use VirtualPC so that I can host copies of Windows XP and Vista in various configurations (high DPI, weird visual themes, etc.) in order to do testing for Paint.NET. I had VMWare up and running successfully, but the moment I tried to run VirtualPC the whole system hung and my awesome techno music started skipping. Bleh!

I managed to find a utility to convert the VMWare “VMDK” hard drive file to a Virtual PC “VHD” file. It’s called, appropriately, Vmdk2Vhd. And now I have my Bugzilla running in Virtual PC on my desktop system.

So there you have it. I use an old version Bugzilla in a virtualized Linux installation which still thinks it’s sitting in a dusty corner of my old university that’s over 300 miles away.

And it works.

And past that, who cares? Right now, I sure don’t. Legacy isn’t all bad, as long as it works and you don’t have to maintain or reconfigure it all that often. I’m basically the sole developer on Paint.NET right now so my requirements are pretty slim for things like bug tracking and source code control (Linus thinks I’m “ugly and stupid” for using CVS … but hey, you gotta admire his passion).

By the way, the quote in the Bugzilla screenshot above reads: “Using exceptions for bounds checking is like driving a car … it’s cheaper to just do a little extra work and stay in-bounds than it is to crash off the side of the freeway and say, ‘Oh the insurance can pay for that.'”

One thing that John Chow has given advice on is selling ads directly. I’ve been thinking about this a bit the last few days and I think it may be time for me to try it out. I certainly have the traffic for it! The getpaint.net website gets over 1 million “page impressions” per month as reported by Google Analytics. The index page does around 400,000, and the download page sits at just under 500,000. Right now I’m using Google AdSense and it is doing very well for itself, at least in absolute terms (never say “no” to free money, right?).

John Chow’s advice says to take the amount you’re earning with AdSense and double it for when you try selling ads directly. The hypothesis is that Google is sharing revenue at a rate of about 50%. His other general advice to diversify your income is one that I’ve implemented as well – albeit by implementing Search that is still provided by Google, and by moving the Help content online and adding AdSense to it (together they added enough to my Paint.NET earnings to buy a Bluray player!).

This could help to significantly diversify my income sources and reduce my reliance on AdSense, which in August accounted for 80% of Paint.NET revenue. It’s not that I dislike AdSense, and I bet the feeling is mutual. I also don’t think I will be banned, but it’s still a significant risk factor — just ask Henry and Wilson about the time they lost out on $200,000 (although they seem to have broken some of the AdSense TOS, such as not having more than 1 account).

I also have advertising space available in the Paint.NET installer. You know when you install the program and it says “Please wait, optimizing…” and there’s a little banner that says “Please donate!” along with one for the download mirror (“This update brought to you by BetaNews”) or for searchpaint.net? I bet I could sell that as advertising space as well. It reaches hundreds of thousands of users per month and is on screen for a good chunk of time.

The only thing I’m wary of is that John Chow also suggests that you create an Advertisers page that list your rates directly instead of saying, “please e-mail us for a quote.” But hey, if I have to disclose revenue to get a huge increase in it, it might just be worth it. John Chow does it every month and when he posts earnings just shy of $18,000 for August, people are stunned and inspired (or, shocked and awed?).

I would probably sweeten the deal by allowing the advertiser (or advertisers, I don’t know how I’ll do things) access to the site’s Google Analytics reports. That way they could see what types of visitors they are reaching, and retarget their ad appropriately if they wanted.

So … comments?

Anyway I’m off to Bumbershoot in the morning*. I’ve never been but it’s supposed to be awesome, and it’s a friend’s birthday too.

* Yeah yeah I posted at 4:30am …

I was told earlier today that an old copy of Paint.NET wasn’t updating, along with the fact that the version was 2.63. This was a version that is pointed at the old Paint.NET website, http://www.eecs.wsu.edu/paint.net, for the update manifest, and that manifest has not changed since 3.0 was released. The problem is that the manifest was pointing at a copy of 3.0 that was sitting on my site, but I had long since deleted the file to make way for 3.01 through 3.08!

The file is back up and so older Paint.NET clients will now update just fine. It will offer you an update for 3.0, but will actually download and install 3.08. So if you know someone who was trying to update an old install of Paint.NET but it wasn’t working, then encourage them to try again!

Sometimes I see news and update sites listing the latest release of Paint.NET with three version fields: 2.7.2 or 3.0.8, for instance.

It’s actually 3.8 if you look at the version resource in Windows.

In truth, 3.08 really is more of a “3.0.8” release: it’s mostly the same as 3.0, except with bug fixes and minor changes that don’t radically change the user experience. Sure, 3.05 added a new effect and 3.07 added on to the Line/Curve tool. But whatever. In truth, I’d prefer to have called these last releases 3.0.1 through 3.0.8. So why didn’t I?

There are really two questions to answer:

  • Why does the version resource say 3.8 instead of 3.08?
    The reason is simply that you can’t actually express “3.08” in the version resource. It isn’t one number with digits past the decimal place, it’s four separate integers. I had to write custom code in Paint.NET so that “3.8” was printed out as “3.08.” Each number in the version can have a value from 0 to 65535, inclusive. So you can have 0.0.0.0 all the way up to 65535.65535.65535.65535. But if you try to specify “1.01” then the “01” just parses to 1 and you’ll get “1.1.”
  • Why isn’t it 3.0.8 then?
    You get four numbers, or fields, for your version. The fields are for the major version, minor version, build number, and revision. The build number and revision number are auto-generated based on the date and time: the build is the number of days since January 1st, 2000, and the revision is the number of seconds since midnight divided by 2. I think it uses UTC time – it doesn’t seem to reset to zero at midnight local time for me J The build number incrementing daily is very useful, and I don’t wish to override that behavior. So, I simply use the minor version field and use custom version printing code in the UI.

    (And yes, I could have the UI print it as “3.0.8” but then it just gets messy if you want to present the full version string: 3.0.8.2708.22795. Wow, that’s a lot of numbers!)

You might think this could become confusing for users, but really it’s probably more confusing for me! Versioning is really about sending a message, or branding if you will. If an update is just a bugfix release, then I’ll increment the version by “0.01,” announce it as such, and that’s what users will see. If I’m adding features and / or significant UI tweaks, then that’s worth a few more version points. If I were to release “3.1” through “3.9” and then skip to “3.10,” then not only would users be expecting major improvements along the way (while not getting them!) but they would probably expect 4.0 to follow 3.9. “Yay, 4.0 is almost here! …. Aww lame. All we got was 3.10, I feel cheated.”

In the past, Paint.NET had a new version every few months. Lately I’ve been trying to get updates out every 4 to 6 weeks. I think it’s working pretty well: users get bug fixes and small features sooner, and the project gets a lot of recurring attention from outside of the “Beta crowd.” However, it’s about time to get back to working on a larger release, so I think I will cut down on the micro-updates for awhile.

Follow

Get every new post delivered to your Inbox.

Join 237 other followers