Distance Debugging Logo

There are a certain set of states or operations within a running program that are not fatal, but are not completely correct or allowable either. I will refer to these as "warning" states, since one of the fairly reasonable things to do in these cases is offer a warning to ther user that they are doing something wrong. Unfortunately, many developers seem to interpret the concept of being fault-tolerant as being silent, meaning that if something is wrong, not only should you gracefully handle and recover from it, but no one should be the wiser.

It's the second part that I take issue with. If you don't publish some kind of message indicating that you have encountered an unexpected state but then handled it, the user will never know that anything was wrong. This is infinitely frustrating when debugging because the output of a "working fine" system will be identical to that of "one or more non-fatal things went wrong" system. I can give two recent examples from my own experience:

  • I mentioned this in yesterday's post: HTML editing and layout is a classic example of silent failure. I can perfectly well understand the browser's desire to be fault-tolerant since in general the user is probably not especially tech-savvy and couldn't care less if the HTML has a bad attribute or two. On the other hand, if you are designing a website, it would be great if a standard browser presented a way to request that it tell you everything that it is ignoring or manipulating to create something sensible. That way you could find all those typos and nonsensical layout instructions that it is cleverly avoiding and stop wondering why it's ignoring your table's "witdh" attribute.
  • I was trying to port a security provider MBean from WebLogic 8.1 to 9.2 and ran into a problem where my new jars were simply being ignored by WebLogic and so I was not able to configure them in the administration console. I assumed the problem was that I had screwed something up when I made the handful of necessary changes for the port. I cranked up every possible debug setting in an attempt to find any hint as to why it was balking, even an "invalid configuration" or something. Finally I just went back to first principles, and walked through my build script and the WebLogicMBeanMaker process to see if anything jumped out at me. I discovered that I had modified a path name incorrectly, and so the MBeanMaker was putting files in the wrong place. This really bothered me because it was actually silently ignoring problems twice: first when the MBeanMaker was run in that it wasn't telling me that it was essentially creating a bogus resulting jar file (I examined the contents of the jar files, which did have some content, but since the MBeanMaker works all kinds of internal magic it wasn't obvious that anything was missing), and then again when the runtime server was skipping the bad jar file. In either case they might have alerted me and saved a lot of blind debugging.

To some extent, I blame programming languages themselves for this phenomenon, which tend to only give you two standard options: report an error, or move on. For example, Exceptions are built into languages like Java at the core level, but Exceptions indicate that something serious enough has occurred that you want to stop what is happening and pop stack frames until you find someone able to handle it, so your only choice is report an error (throw an Exception) or not (move on). It would be nice if there were a third option, throw a Warning, let's say, that didn't change the stack state at all. You could choose to wrap a block of code with a warning handler and do something sensible, or ignore it and it would be passed up the stack until something did, but after the handler executed, you would return the stack to the state it was at the time of the Warning. In Java-esque code:

public void doSomething() {
try {

doSomethingSemiDangerous();

doTheNextThing();

}
warn(Warning w) {

reportWarning(w);

}

}

public void doSomethingSemiDangerous() {

startActivity();

if(variable == badState) {

throw new Warning();

}
continueActivity();

}

Please excuse the weird blockquote formatting; that is my lazy approach to indenting the method bodies for clarity.  In the second method, continueActivity() would always be reached, because the stack state is preserved during the Warning handler chain, and the same would be true of doTheNextThing(), which would be reached whether or not the warn{} block were entered. This really isn't anything fancy in terms of a programming language. In Java, it could be implemented fairly easily with Aspects. The idea is simply to encourage developers to report not just problems that require aborting the current process, but also problems that don't require it, without needing to do something clunky like pass a callback around to report warnings, or adopt some writing to stdout convention, or tack on logging calls everywhere. Hopefully, if this notion were built into the fabric of the language, developers would do more of it.

I implore the development community to spend some time considering the users of your systems that need this information, and make a concerted effort to report non-fatal problems in some form. I think ideally, it would entail adding a well-known setting that can be enabled so that non-technical users don't get spooked by the messages, but those who need them can get at them. If you want write a language with built in support for "Warnings", so much the better.

Google has recently announced a new open source crash reporting client-server library called Air Bag. As described in this article, the idea is to replace the closed source TalkBack crash reporting system currently used in Firefox, to begin with, and then extend it further to other applications. The idea of a client-server crash reporting system is not new. On Windows, the Dr. Watson "Application X has quit unexpectedly. Send report to Microsoft?" box is somewhat ubiquitous, and there are equivalents on other operating systems such as Bug Buddy on Linux.

While Google will probably make a few friends with this new reporting system, I have to wonder whether these types of blind crash reporting tools are really all that valuable. They suffer from some serious problems depite their advantages:

  • The staggering amount of data that is collected
  • The lack of context information
  • User annoyance and perception

One of the links in the side bar is to the Cooperative Bug Isolation Project, which aims to resolve the first issue by applying statistical techniques to a cluster of reports to help isolate faulty regions. I'm not really doing the project justice, but I recommend checking it out.
In terms of the second issue, I'm not really sure what there is to do. I've seen some debate in the bug reporting tools' mailing lists about whether or not to ask the user what they were doing at the time of the crash. The pro is that it provides more information, the con is that it forces a disgruntled user to stop what they were doing for even more time, and probably increases the likelihood that they will send no report at all. That is true of pretty much any non-automated data collection technique.

The third issue relates to getting the user even more upset about something that they were annoyed with in the first place. For example, every so often, my Windows computer reboots for no apparent reason, always when I am not using it (like in the middle of the night). I only know because when I come in the next day, it is sitting at the login screen instead of where I left it. When I log in, the little "Windows has encountered a serious error!" box pops up and asks if I want to send a report. The thing is, I don't honestly believe they care, and I wonder if that's something that a lot of people submitting these bug reports come to believe. It crashes all the time and I'm constantly sending these bug reports, yet nothing ever seems to get done about it. It reinforces the notion that developers don't have any desire to fix problems.

So what can be done to improve centralized crash reporting?

  • Something I've been thinking about for a while is introducing the notion of a "semantic" stack within a program. Essentially this would be adding code markup that gives a series of method calls a semantic tag like "Opening an Existing Document". Then, in addition to providing the raw stack trace, the system could dump out the semantic stack and also get a sense of what the user was doing in more context. It might look like "Clicked Existing Document Menu Item -> Selected "Foo.txt" from File Chooser -> Crash"
  • Using some of the statistical techniques from cooperative bug isolation, allow a user to look at how common their particular problem is, and from that get a sense of its priority. If each crash that was produced was associated with a key that could be used to query a database, then the user could submit that key to a website that showed them how that bug clustered with other reports. If it is an outlier, perhaps their own system is to blame. If it is one of the most frequently reported, it will likely be fixed.
  • Perhaps link the bug reporting system in with the automatic update checking that many systems do (like windows automatic update or pup on Fedora) and tie incoming updates to specific bug reporting clusters/keys. That way, a user knows that when they get a particular update, it will fix a particular issue. This helps with user perception that developers are paying attention.

Crash reporting as it stands seems to be somewhat useful for developers, but mostly a pain for users. I think that a few small changes could go a long way towards improving its utility for both sides.

Sorry about the week-long absence. I took a family vacation and was out of the clutches of the internet for a few days. While it was a nice change of pace, even when I am ostensibly on vacation, I tend to stumble across interesting problems, or in this case, solutions to problems.

I always wonder about credit card fraud prevention when I am on vacation. Basically, I normally have a bunch of charges from one physical location, and then I suddenly have a bunch of charges in a new physical location. On it's face, it seems like it would be very difficult to separate out the vacation credit card usage pattern from the stolen CC# pattern. However, using my credit card in another state rarely seems to trigger a fraud alert from my credit card company, while buying a computer that is shipped to the billling address does, for whatever reason. I guess this is what the bayesian or whatever predictive model they use tells them. This always concerns me though.

This trip, I stumbled upon what I think is a new wrinkle in the out-of-state credit card fraud prevention system. When I went to fill up my rental car's gas tank, I used my card as normal, only this time it asked me for my zip code. At first, this prompt had me totally befuddled since I was expecting it to simply ask me to "lift nozzle" as usual. Then it occurred to me: what an ingenious way to prevent out-of-state usage of a stolen credit card number! If my credit card is stolen and used by some unknown third party, the likelihood that they will have any idea of the billing zip code for that card is almost zero, while a legitimate card user will have a close to 100% chance of knowing it (barring odd edge cases like borrowing a friends' card). It's a great example of getting something right that supposed security systems screw up all the time: a security policy that is at most a small nuisance to legitimate users, but a significant hurdle to illegitimate users. I might be reading too much into this simple prompt, but I'd be curious to hear if any readers have had this experience in their day-to-day credit card usage, and if it is a relatively new enhancment.

Testing is a poorly understood concept within the world of software development, especially unit and blackbox testing. I think the fundamental problem is in understanding the purpose of testing. When I first started as a developer, I thought of testing as something that someone else does to my code to find problems in it, and I fell into the trap of seeing myself as adversarial with testing. Now having built systems with large, extensive unit and functional tests written concurrently with the code, I have a totally different conception of testing. I treat my test cases like formal requirements. If all the tests pass, then I am meeting my requirements. If the system breaks and my tests didn't catch it, then my requirements were not completely specified and I need to modify my test cases.

This philosophy has some interesting consequences. First, developing, testing and debugging end up working in lockstep: every time a feature is added or bug is found, a test is added for it. One thing I really like about Ruby on Rails is that this idea is built in to the "generator" mechanism in that it creates test fixtures for every controller that you create. Second, I don't code "scared" anymore. I define coding scared as a refusal to make changes or add features because you are afraid of breaking your system in unexpected ways. I hated coding scared, but it's generally where you end up without tests. Since you have no way of quickly and comprehensively verifying the result when you make changes, you have rely on expensive and time-consuming manual usage to tell you what you've done wrong. Finally, this approach has made me much less resistant to change. In a traditional software development model, you spend a lot of time trying to determine how risky is and whether the risk is worth it. I worry about that less than I used to, because if something is going to screw my system up badly, I'll know it right away, and with good iterative development practices, I'll know exactly where things went wrong.

In my head, I imagine each piece of software as being built on a giant empty surface. As I make changes and add features to the system I am drawing and redrawing a shape on that surface. The problem is, the shape needs to avoid certain regions of the surface, regions which represent bugs or other problems. The tests that I write are little fences  that keep me out of those regions, and as I write more tests, the shape that I can fill in becomes more and more clear. That is the true purpose of tests: they are the best way to constrain our systems and guide us to the ideal shape.  With the fences in place you can worry less about making missteps and worry more about how to fill in the correct regions.

There is a culture of the job interview "riddles" among the big tech companies like Microsoft and Google. You can find a collection at this site if you are interested. These are all fine and good, but I always have an important question: what does this have to do with the job? I've railed about the need for authentic assessment in this space before, and this seems to be the exact same problem. Why have me do things in the interview that I won't do in the job? They seem to be falling into the IQ trap, assuming that you can design a test that somehow gets at the fundamental kernel of general intelligence when a) you are very likely only testing a very specific set of skills and b) high-stakes testing almost never tells you anything about a subject's ability. Maybe there is a decent correlation between people who excel at those riddles and people who excel within the company, but I kind of doubt it, and I'm guessing it tends to eliminate good candidates who are just bad at on-the-spot puzzle solving.

So, can I do any better? What would I ask a potential hire for my notional debugging company?

  1. Do you own a set of Torx screwdrivers?
  2. In as much detail as you are able, describe one of the following:
    1. What happens between the time that you enter the URL of a website into your browser and that page displays (or fails to display)?
    2. What happens between the time that you enter an SQL query into a database and the results are returned to you?
    3. What happens between the time that you press the power button on your computer, and the login screen appears on your desktop (in the OS of your choice)?
    4. What happens between the time that you hit send on an email message and the time that the recipient receives the message?
  3. This computer won't boot up. What's wrong with it?
  4. Are there devices in your house that have either software or hardware that they did not ship with, and how and why did they get modified?

The justifications:

  1. This might seem like a silly question, and it borders on a non-authentic question. However, the only people I've ever met who own them are people who like to open things up and find out what's going on inside.
  2. This is the first big hurdle. I am consistently shocked at the gaps and misconceptions people have in their technical knowledge. I figure that a good interview subject could probably spend upwards of an hour on any one of these questions, and the choices are broad enough (networking, databases, operating systems/computer theory, general IT) and common enough that a potential hire should be able to speak about at least one of them at length. They all have interesting possibilities for discussion along the way, and the questions that they ask in response would be informative as well.
  3. This is the critical portion of the interview, and the key to authentic assessment since this is really what they would be doing. I don't care so much if they manage to fix it or not, but I care about how they approach the problem. Do they start by asking me some background questions, or do they just pop the thing open (both of which might have merit)? Where do they start looking if they pop it open? If they ask questions, do they seem to be trying to work on a theory? Are they just totally overwhelmed or bored by the task or do they seem excited to work on it?
  4. Again, bordering on non-authentic, but this speaks to the other thing I look for besides skill: enthusiasm. I like to say that you can teach skill but you can't teach enthusiasm. Someone who hacks the stuff in their house or likes to have hacked stuff in their house is interested in learning about and fixing things independent of their work, and that speaks to engagement with the subject.

So that's my interview in a nutshell. I think it would appropriately identify the people with the requisite skill and interest for a job debugging full-time.

Besides a knack for finding bugs, I also seem to have the ability to find missing "stuff" like a misplaced document or a piece of clothing. My wife on the other hand, for all her other excellent qualities, is terrible at finding things to the point where it has become something of a running gag between us. Additionally, I think that my finding skills have caused hers to completely atrophy and she will make only a token search for something before turning it over to me. As I've looked at the difference in the way that we each approach the process of finding, I've noticed some key differences that have led to me believe that one of the underlying skills for good debugging is a good sense of how to find something. Here are few differences between us:

  1. Confidence - When I go to look for something, I am certain that it will be found. My wife is always convinced that it is lost forever and I think that colors her approach.
  2. Systematicity - I check and recheck a sequence of areas moving from area of highest likelihood to least likelikhood. My wife tends to look quickly through a series of places hoping that it will be easily found and often fails to recheck high likelihood areas. This probably goes back to the confidence issue.
  3. Region Expansion - If after exhaustive search of likely areas I turn up nothing, I will expand the search area to include places where it "couldn't possibly be", and am not surprised to find things in those places. My wife always tells me I'm "wasting my time" since she "never would have put it there." But after turning something up in an unusual place, she generally has a perfectly good explanation for how it wound up there.

Believe it or not, I think I was actually trained to be a good finder at a young age. For instance, my mother created a game called "hiding in plain sight" where I would leave a room and she would hide a very conspicuous item, like a large doll or a colorful block, somewhere in the room. The only rule was that the item had to be completely visible as you walked around the room, in other words, it might not be visible when you first stepped in, but it would never be in a drawer or buried under a pillow. This should give you an idea of what my childhood was like.

Anyway, you might think this greatly limits your possibilities of places to hide something but in fact it really does not. You would be surprised how hard it can be to find even a very noticeable item in a room full of other stuff. I remember going through a series of increasingly sophisticated strategies. At first, I just kind of walked around hoping to spot the item. Then I started focusing more on figuring out where the good hiding places were and checking them first. I also got better at picking out the shape and color of the item and focusing on those features. As I got better, my mom also came up with little things to make it trickier, like hiding the item in the same place in succession, or adding similar items to the room.

The tricks I learned from this game have carried over into my work finding bugs. I always like to reiterate that debugging is a teachable skill, and I think it's clear that many of the underlying skills that constitute the process are teachable as well.  As I do this exploration of debugging concepts on this website, I will try to illuminate and discuss these underlying skills as well.

I have one rule of thumb that serves me well in my general work as a developer, but also as debugger: nobody reads anything. Never. This is generally accepted wisdom among software people, with the conclusion being that you need to build software in such a way that a user could stumble through it without ever having to read a word of documentation. Overall, I think this has a detrimental effect on software because it forces interfaces to focus too heavily on certain stereotyped ways of working at the expense of a better overall task-oriented interface. However, it has become a necessary evil with the alternative being that no one will use your stuff because it's too "confusing".

As a backlash to this mindset, I have cultivated an almost obsessive tendency to read every piece of documentation that comes with things that I purchase or whenever I'm asked to look at a problem. This is a very simple trick that often makes me seem far more capable than I actually am. For example, most digital thermostats have fairly clear instructions right inside the cover about how to set the basic start and stop times, temperature, etc. But most people don't ever look at that, instead attempting to puzzle through it by randomly pressing buttons until they get where they think they need to be. Having a reputation as someone who can "figure things out", I am often asked to "decipher" someone's new themostat and get it set up for them. I start by flipping open the case and reading those tiny instructions which usually tell me everything I need to know. I then proceed to follow the directions and set the thing up, to the delight of the asking party. They say, "how did you figure it out so quickly? I couldn't get anywhere with that thing!", and unwilling to share my secret I say something like, "oh, I've seen one like this before".

Reading the available documentation is a stunningly simple yet effective technique in any debugging situation. There is often quite a bit available that no one has bothered to look at thinking that it would be tedious or a waste of time. I can't guarantee that it won't be, but you will be surprised at the nuggets of wisdom buried deep in user and administrator manuals. Even if it is not helpful for the situation at hand, the information may prove useful in the future, and it is a great habit to develop.

Within the field of cognitive science, it has been asked: what constitutes a quality analogy? Psychologist Dedre Gentner has proposed the Structure-Mapping theory that in essence says that good analogies are made by aligning low-level elements of two situations or problems and then using these low-level alignments to build up "higher-order" alignments between the situations, and the higher up you are able to find alignments built from previous levels, the better the analogy becomes. We fail to make good analogies when we focus too much on the surface structure and not enough on the actions and events taking place. For instance, claiming that King Arthur is a lot like King Lear because they both are about Kings is not a good analogy because there is no alignment between their actions and events. Claming that King Arthur is a lot like Michael Corleone while not a fabulous analogy, is at least better as you can find relationships on which to build the analogy, such as that they were both leaders that came to power through an unexpected event (pulling sword from stone/killing police chief).

This theory relates to work done in education on the difference between expert and novice thinking. For example, novices and experts are shown a pair of physics problems each involving a "wedge" but involving different physical laws. When asked if the problems were similar, novices were likely to say, "yes, they both involve a wedge". The experts were likely to say, "no, they both have a wedge, but here I'd have to use Newton's second law, and in this one you're looking for the spring constant". The problem is that the novices focus on the surface similarity instead of the underlying principles involved and thus make a false analogy between the situations.

I think this same mistake is made when trying to fix bugs. We get too caught up in the "surface" features of the bug such as the visible effect on the system, and the place in the code where error is happening. One of the harder things to do is use that information to look for the deeper relationships, connecting the dots of the visible effects to nominate core underlying causes. Bug tracking systems only reenforce this notion by making symptoms prominent, with little or no place to track causes. This is understandable in the context of someone reporting a bug who wants to look for problems similar to theirs. However, when symptoms are used as the primary indexing attribute of bugs, it starts to conflate a variety of unrelated problems under a common heading. This can lead to both issues of reputation when a person's bugs keep getting reopened despite the fact that they fixed the issue completely and correctly but the bug happened to have a common symptom, and issues of disjointedness, where patches to fix symptoms are constantly applied rather than identifying and addressing an emergent pattern that points to a single underlying problematic component. Only by digging deeper into problems and creating those "higher-order" relationships can we eliminate these root problems that give us ongoing grief.

Everywhere I look nowadays I see yet another plugin architecture. I have nothing against plugins. I think that the concept of componentizing software is powerful and a good goal for a system. However, in the desire to become flexible and end-user extensible, I think that many projects have gone too far in their desire to piece things together via an aggregation of plugins rather than deciding to draw the line and refuse to allow change beneath a certain threshold.

Ultimately, plugins have to do with changepoints, and nothing will kill a piece of software faster than too many changepoints. I don't know where the term changepoint originated, although I hear it often in OO Design circles. Essentially, it is a place in your system where the design expects multiple possibilities. This could be because there might be multiple ways to accomplish something and you want to offer options, because end users might have their own solutions and want to extend the system themselves (i.e. via plugins), or it might be because you simply don't know at the time how things should be done so you are offering your future self some flexibility.

It is generally assumed that each changepoint that you build in has a cost in terms of system complexity and hence maintainability, programmer effort, and very likely performance. Often, these problems are easily offset by well-placed changepoints. For example, the Strategy pattern is about creating a change point to allow for different algorithms to be swapped in without requiring a design, or even code change. The benefits of the right algorithm can significantly outweigh the slight performance hit and code complexity that result from using a Strategy pattern.

On the other hand, we can be lured in by one or two successful tradeoff gambles and start putting in changepoints for less convincing reasons. Sometimes we put a changepoint in because we are lazy and don't feel like thinking about what the "correct" answer is and just offer some flexibility instead. Other times, while we only have a single clear system function in mind, we put in a changepoint because we think that someone somewhere might want to do it differently, despite the absence of any evidence to suggest this degree of flexibility. This is the kind of thing the extreme programming people are always yelling at us about, and I happen to agree with them on this point. Generally, the cost of software change is not as high as we think it is, and the amortized cost of paying for a changepoint that are not using can be very high in the aggregate.

That's where my main criticizism of plugin architectures comes in. Many plugin systems that I have worked with have somewhat grandiose notions of what people should be interested in changing and pay the cost in being brittle and hard to configure, even to do their primary function. It's too easy to accidentally disable a plugin, or fail to have the right ones installed, or have one of the plugins crash and take the whole thing down. Beyond the difficulty of properly trapping and reporting errors coming from code that you might never see, there is the problem of how you figure out where the problem is and who's "fault" it is. Is it the architecture's fault for not plugging things together correctly? Is it the plugin's fault for being flaky or not playing nicely? Is it my fault for botching the configuration? All of the above?  I am willing to put up with a certain amount of this from a system in exchange for power and configurability, but I often feel like I am just using an over-engineered system that does only one thing well despite the fact that it was designed to do much more.

In my quest to build a better bug tracking application, I decided to try out Ruby on Rails since I'd been tracking it for a while, seen a demo at BarCampMilwaukee that piqued by interest, and had been itching to build something with it. I am only passingly familiar with Ruby syntax and development techniques, but the claims are that you can get up to speed with it very quickly and put together the rudiments of an application with very little effort. I think some of those claims are justified, and some of them are not, but here is my experience thus far.

  1. I do most of my development in Eclipse, and at the talk I saw, they mentioned something called RadRails which is an Eclipse plugin although the didn't use it. I installed both the Ruby Development Tools (RDT) plugins for Eclipse and RadRails, which are both free.
  2. Unfortunately, while Ruby on Rails has a big community with lots of users, RadRails is just starting to get some traction so I had to kind of muddle through it and piece things together by taking what I was seeing from a generic Rails tutorial, the examples on the RadRails site, and some leaps of faith. Essentially, RadRails allows you to issue the necessary rails commands to do things like launch servers, create models, etc. from inside of Eclipse, along with providing the basic Ruby and Ruby HTML editing facilities using Eclipse's powerful IDE framework. Overall, I've found it to be very useful after the initial learning curve of trying to learn Ruby, Rails, and RadRails all at once.
  3. The other trick was getting a database set up to use. The recommendation is MySQL, and that makes sense for me since this site is hosted with DreamHost, and that is what they provide their users, so if I want to eventually deploy my application here, it will be possible. Since I run Fedora, I did a yum install to get the necessary stuff, and grabbed the mysql GUI admin tools from their website. I created a new database and threw some tables together that seemed sensible.
  4. Now it started to get a little ugly. First of all, it was floundering in RadRails because when I tried to create a new application, it was claiming success and then silently failing (i.e. it wouldn't create the standard directory structure for a rails application). I discovered that I needed to install the rails code directly; that is not part of RadRails. In retrospect, this makes perfect sense since you need a version compiled for your OS, but I was totally stumped at first. Luckily, Ruby offers a nice installer tool like yum called gems, so I could just say 'gem install rails'
  5. Unfortunately, this failed with some error about a missing library. Googling around, I figured out that I also needed to install some other dependencies that gem can't resolve. Check out Installing Ruby on Rails on Fedora Core 5. Seemed to work fine for FC6 as well. That fixed the dependencies issues.
  6. Now I started to roll. I followed this tutorial, linked from the main Ruby on Rails website, only instead of creating a recipe tracker, I just created stuff for my bug tracking application. This worked fine up to a point.
  7. The rails motto is convention over configuration. The idea is that by using certain conventions such as file structure, database table naming, etc. the system can infer connections and behavior. This is good for productivity, but poison for debugging problems in my opinion. The problem is, when you don't know the convention very well, you can break from it silently without knowing. I did this with my database tables. Rails uses a standard model-view-controller setup where by using naming conventions in your model, it will automatically hook up to your database tables. That's pretty cool, except I didn't know that when I created the tables, and the convention is not (as I would have expected) model name == table name. Instead, rails tries to be cute and un-pluralizes your table names in the model name. So when I created a table to hold the main bug info called casefile, I then created a model called casefile, but they couldn't hook up because rails was looking for 'casefiles'. Even worse, since I am a Java programmer primarily, I called my model CaseFile, which rails mapped to case_files knowing that I meant two words. I figured this out eventually and changed my table names, but it's sort of an odd convention to use as the default in my opinion. I assume there is some way to override this if for example, you are hooking up to legacy database tables, but I haven't gotten that far.
  8. I started to get really screwed up when I tried a custom view to edit new bugs. I had a weird circumstance where the default value would update data into the database with no issue. However, my custom view simply did nothing, and produced no error. Googling for 'rails update fails', etc. produced nothing relevant. After comparing the HTML produced for the default view with my custom version, I discovered the problem. I guess rails uses a convention by which if you specify the name of a form widget like [
    ], it binds the value of that property in the form to that object, and then to the database. I got screwed up because I was specifying the name as the variable that I had bound to the current case file. So in the corresponding controller, I created a variable called @case_file, and then I could say things like "Name: <%= case_file.name %> in the rhtml file. That worked fine, but when I tried to create a widget like: input type="text" name="case_file[name]", it just ignored it, because it wanted name="CaseFile[name]" since that was the name of the model object, I assume. While this wasn't exactly an error, I was hoping that rails would have said: hey, you are specifying a binding to an object from the model that doesn't exist.

So, in conclusion. I got something up and running in a couple of hours worth of effort, but I spent about 50% of that time debugging one weird issue that should have been caught by the system, and stemmed from the whole convention over configuration bit. The last thing I want to mention is that I don't like the fact that stack traces and other messages don't appear in the server window, but instead are only dumped to a log file that corresponds to the mode you are in (like development.log). This is a weird convention that will take some getting used to. I will post more on this topic as I make more progress on my application.

Syndicate content