What is the purpose of (Apache) putting inode into an ETag? - apache

There are plenty of articles on the web detailing why you might not want to use Apache's default inode-mtime-size format for ETags.
But I have yet to read anything on what might have motivated the inclusion of inode for Apache in the first place. On the face of it, it only seems useful if one needs to be able to differentiate between octet-for-octet facsimiles of the same resource, but this is surely counter to the very purpose of ETags.
Apache's authors are not known for their sloppy handing of internet standards, so I feel I must be missing something. Can anyone elaborate?
EDIT: I ask this here rather than on ServerFault.com because I'm implementing a web server rather than administering one. To read more about why it's a bad idea, see e.g. here or here. All such articles recommend the same thing: remove inodes from your etags. The question is, is there any advantage whatsoever to them being there?

It seems like the kind of thing one could easily do by a wrong guess for what's the common case, or by preferring correctness over performance, by default, whenever there's a shred of doubt.
Allow me to make up a story about how it might have gone:
They decide early that a hash/checksum on the contents is a bad idea for performance reasons. "Who knows how big the file might be? We can't recalculate those all the time..." So they decide size and date get you pretty close.
"But wait," person A says, "nothing guarantees you don't have a file size collision. In fact, there are cases, such as firmware binaries, when the file size is always the same, and it's entirely possible that several are uploaded from a dev machine at the same time, so these aren't enough to distinguish between different contents."
Person B: "Hmm, good point. We need something that's intrinsically tied to the contents of the file. Something that, coupled with the modified time, can tell you for certain whether it's the same contents."
Person A: "What about the inode? Now, even if they rename the files (maybe they change "recommended" to a different file, for example), the default etag will work fine!"
Person B: "I dunno, inode seems a bit dangerous."
Person A: "Well, what would be better?"
Person B: "Yeah, good question. I guess I can't think what specifically is wrong with it, I just have a general bad feeling about it."
Person A: "But at least it guarantees you'll download a new one if it's changed. The worst that happens is you download more often than you need to, and anybody who knows they don't have to worry about it can just turn it off."
Person B: "Yeah, that makes sense. It's probably fine for most cases, and it seems better than the easy alternatives."
Disclaimer: I don't have any inside knowledge about what the Apache implementers could have been thinking. This is all just hand-wavy guessing, and trying to make up a plausible story. But I've certainly seen this kind of thing happen often enough.
You never know what it was that you didn't think of (in this case, that redundant load-balanced servers serving the same files was more typical than having to worry about size+time collisions). The load balancer isn't part of apache, which makes it easier to make such an oversight.
Plus, the failure mode here is that you didn't make perfectly efficient use of the cache (NOT that you got wrong data), which is arguably better, though annoying. Which suggests that even if they did think of it, they could reasonably assume somebody with enough interest to set up a load balancer would also be ok with tuning their configuration details.
PS: It's not about standards. Nothing specifies how you should calculate the etag, just that it should be enough to tell whether the contents have changed, with high probability.

Related

When you write your code, do you deal with errors proactively or reactively?

In other words, do you spend time anticipating errors and writing code to get around these potential issues, or do you write the code as you see fit and then work through any errors on an issue by issue basis?
I've been thinking a lot about this lately and I'm very much a reactive person. I write my code, give it a whirl, go back correct error and repeat until application works as expected. However a friend of mine offered that he spends time thinking how each line is interpreted and fixes errors before they occur.
I must point out that re-active is pure PRE-live. I definitely make sure my application is working before it goes live.
There should always be a balance.
Too many error checking is slow and leads to garbage code. Not enough error checking makes your program crash on edge cases which is not very good to discover after having it shipped.
So you decide how reliable some piece of code should be and implement error checking accordingly. Some test utility can be not very reliable - less error checking. A COM server meant to be used by a third party search service in deep background should be super reliable - much more error checking.
I think asking this in isolation is kinda weird, and very subjective, however there are obviously a bunch of techniques that permit you to do each. I tend to use these two:
Test-driven development (this would seem to be proactive)
Strong, static typing (reactive, but part of a tight iterative development cycle, as in, it's enforced by my ML compiler, and I compile a lot)
Very occasionally I swerve into the world of formal verification of programs. That's definitely "reactive", but if you think a little more up-front, it tends to make the verification easier.
I must also say that I value a lot of up-front thought in programming. The easiest way to avoid bugs is to not write them in the first place. Sometimes it's inevitable, but often a little more time spent thinking about the problem can lead to better-quality solutions, and then the rest can be taken care of using the kinds of automated methods I talked about above.
I usually ask myself a bunch of what-ifs when coding, like
The user clicks the button, what if they didn't select a date?
The user is typing in the search box, what if they try to type html in there?
My label text depends on a value from a shared drive, what if it's not mapped?
and so on. By doing this I've found that when the application does go live, there are a ton fewer errors and I can focus on fixing more obscure bugs instead of correcting conditions that should have been in place to begin with.
I live by a simple principle when considering error-handling: garbage in, garbage out. If you don't want any garbage (e.g. invalid input) messing up your software, you have to find all the points in your software where it can get in and handle it. Of course, the more complicated your software is, the harder it is to find every point of entry, but I feel that the more you do up front the less reactive you will need to be later on.
I advocate the proactive approach.
I try to write the code in that style which results in maintainable and reliable code
I use the defensive programming techniques to prevent stupid errors in code due to my loss of attention and similar
I design the database model according to the fortress principle, SQL code checking for results after each singular operation
I think of potential problems that can happen with that part of the code and I account for that. Not for every possibility but for major ones I can think of right now.
This usually results in software operating rather smoothly. At times it even surprises me but that was the intended goal, so here we are.
IMHO, the word "Error" (or its loose synonym "bug") itself means that it is a program behavior that was not foreseen.
I usually try to design with all possible scenarios in mind. Of course, it is usually not possible to think of all possible cases. But thinking through and allowing for as many scenarios as possible is usually better than just getting something working as soon as possible. This saves a lot of time and effort debugging and redesigning the code. I often sit down with pen and paper for even the smallest of programing tasks before actually typing any code into my editor.
As I said, this will not eliminate all errors. For me it pays off many times over in terms of time spent debugging. Another benefit is that it results in a more solid and maintainable design with fewer bugfixing hacks and special cases added on later. But in any case, you will have to do a lot of debugging after the code is done.
This does not apply when all you want is a mockup or rapid prototype. Also practical constraints such as deadlines often makes a thorough evaluation difficult or impossible.
What kind of programming? It's impossible to answer this in any general way. (It's like asking "do you wear a helmet when playing?" -- well, playing what?)
At work, I'm working on a database-backed website. The requirements are strict, and if I don't anticipate how users will screw it up, I'm going to get a call at some odd hour of the day to fix it.
At home, I'm working on a program ... I don't even know what it'll do yet. I can't deal with 'errors' because I don't know what 'an error' is in this context, because I don't know what correct behavior is going to be. The entire purpose of the program can and frequently does change on a timescale of minutes to hours, so even a couple minutes spent thinking about errors this early is a complete waste of time. (It's even worse than browsing SO, since error-handling adds lines of code.)
I guess the only general answer is "I do what makes sense in terms of saving time in the long term", which is, after all, the whole reason to use machines to do work for us.

Is "You break it, you buy it" the best policy?

There is a subtle reason why it might not be good: Sometimes, the blame for breaking something really should be placed on the individual who wrote fragile code without automated tests, not the one who broke their code by making a should-be-unrelated change somewhere else.
One imaginable example is when someone programs against an interface in a way that assumes behavior specific to the implementation du jour, but not guaranteed by existing contracts. Then someone else makes a change to the implementation that fits in the contract, but breaks the depended-on code. No tests fail because no tests are written for the depended-on code. Who's really to blame?
The purpose of this isn't to blame people, but to understand responsibilities, and if "You break it, you buy it" is really such a good policy.
EDIT: I really worded this poorly. I meant it to be about how to write correct software with respect to dependencies, including hidden dependencies. I meant this to be a question of what a programmer's responsibility is to avoid bugs, not what to do when a surprise bug is found. BUT, since so many answers have been given already, I'll let the question stand as-is and indicate an answer accordingly.
I think you have nothing to gain and everything to lose by promoting an atmosphere of blaming and finger pointing. When something is broken, you should assign it to the best person to fix the problem, whether that is the last person to touch that area because he or she knows the area, or to the person who wrote it first so knows the design philosophy best, or even just the person without anything more pressing to do.
"You break it, you buy it" makes sense in terms of breaking builds, not more serious problems.
If you put the build into a state where it can't compile, or run basic tests, you are blocking other people's work. If you can't see a quick and simple fix (because you introduced a quick and simple bug) then just roll back your changes (perhaps with local copies of what you'd worked on in the meantime) and commit.
If the fact that you broke the build is ultimately due to a wider issue, then deal with that wider issue, whether by fixing it, reporting it, or assigning it.
Short term, the person who made the code-base unworkable quickly makes it workable again. Long term, the best person for the job (balancing different factors) does the job.
Fixing it is the purpose not laying the blame. Suppose the orginal author of the fragile code has moved on, who would own the problem? Suppose he or she is simply assigned to another project? The person who ran into the problem needs to be the one who owns it until it is fixed, he or she is the person currently there and currently assigned to the task of making changes to the application.
Now if you know the code was created with a problem that should be avoided in the future and the orginal developer is still there, it would be a good thing to let him or her know about the issue and why it caused a problem, but ultimately the person who ran into the problem is the one who will need to fix it to get his new code to work.
I would say that assigning ownership to the disaster prone may not always be the most productive strategy. It is likely its own reward.
The last person to touch it should be at fault, Refactoring is a large part of software development, if somebody touched the code and did not properly document, write and test the code than that is on them. As part of the estimate, the time to properly put the code in better shape than it was found should be included.
That being said, if something does not work, the whole team should take the fall.
In an environment where people have to work together, cooperation should be of greater importance than placing blame. If someone`s module is fragile and if his/her peers agree that something should be done about it, a good team-player would fix the problem; that is, he would write unit tests .etc
In any case, a programmer's own code is ultimately their responsibility, and if they can't handle the responsibility of making their code cooperate with that of others, then they rightfully must take the blame. But not before giving them a chance or two to clean up their act.

Cocoa app - security issue

I've a question about a good way to protect a bit my cocoa app from piracy. I know that this is impossible!
So, in my app I've an isRegistered() method that runs every time the user launch the app.
This is called from the applicationDidFinishLaunching: App delegate. So if this method returns true, the app continues to execute the code, otherwise an Alert appears saying that the app is not registered and there are xx day to buy a license.
This is a good way? Because, I have no experience in this.
Thank you in advance for your help!
SOLVED
First of all, thanks to everybody! I think the same thing: any copy protection can stop the piracy. I'm trying only to solve this little bug, even if I know that someone will crack my app again.
However, it's true - the best thing is to improve the app and not waste the time to try make the piracy protection more efficient.
The solution you describe requires almost no expertise whatsoever to crack. It is trivial to change your isRegistered() function to always return true. Thus, the effort required to circumvent your protection is a tiny fraction of the effort you would have to spend implementinging all the infrastructure to support users purchasing registration codes.
In other words, you're not getting a good return on investment. There is some debate over whether the return on investment implementing piracy protection (rather than improving your product) is ever good enough (because you pit yourself against people who have nothing better to do than prove they're cleverer than you).
One good way to redress the balance of return on investment is to use pre-existing code such as AquaticPrime. That way, at least you won't have spent so much time chasing rainbows :)
I am not in shrink-wrapped software business but my friend is. And his observation after 10 years of selling his product was that it makes no sense to create too sophisticated protection because always some one will hack it. You are alone and world is infinite. It is better to invest time/money in improving your software than working on copy protection.
Also keep in mind that around 10% of will never steal and other 10% will always try. Just make sure that those 80% is able to buy your product without any other mayor obstacle. Than you could ignore those nasty 10%. Actually it is a quote from Joel Spolsky IHMO.
So your solution seems to be completely OK from technical point of view and just stay with it.
it's almost never worth implementing your own anti-piracy system, because you'll almost always spend a lot of effort on something which can then be broken very easily. Rely on a shared implementation - in this case a framework like AquaticPrime (lots of people on the macsb mailing list recommend that one) - and you're effectively relying on the framework being good enough to protect your own app as well as all the others.
The code signing framework on Leopard and later allows you to sign your code such that if it's ever tampered with, it will refuse to run - see the documentation of the kill option in the manpage.
This is a good question. Having read the answers, I think what BitDrink was really getting at was this: we know that an isRegistered() function is dead simple to hack. With the understanding that any protection system eventually will be hacked, what are some strategies for writing a function that's harder to hack than an isRegistered function that returns a boolean?
Fundamentally, any copy protection system will eventually have something that looks like this:
if (program is registered)
let the program continue
else
nagging message
end
Any hacker with a copy of GDB will eventually find that first line and write a tiny little patch to strip it out. Most copy protection systems focus on security through obscurity, i.e. making that line hard to find. You can also make this system more robust by signing the binary and checking the signature, but you'll just add another hoop for the hackers to jump through. They'll eventually find your public key and change it to their own public key so they can replace your signature. However, I believe this will significantly slow them down. Leopard offers a code signing utility, but I don't know if it can be used to prevent incorrectly signed applications from running at all.
There's no perfect solution to this problem, but there are two main things to remember:
your registration system will be broken. There is absolutely no way around this.
your reigstration system is a barrier between the user and your program. You should optimize for the (hopefully majority of) legitimate users and make this as easy to do as possible.

What are effective ways to log and track programming mistakes?

On occasion I've heard people discuss the benefits of keeping track of programming mistakes, if for no other reason than it increases awareness of common errors. I've started to keep a list of bugs that I find in my code, along with what could have led to them. The main question I have is this:
What information related to my mistakes should I be keeping
track of so that I can improve as a
programmer?
And a couple other questions related to this:
How do I use this information once I start logging my mistakes?
Is tracking mistakes truly beneficial?
This is only useful if you are actually vigilant with tracking and reviewing. When I was working on a team, no matter how much documented that for example our servers in the production environment were natted and would not be able to resolve their own domain names or public IP addresses, every 6 months, I'd get a call at 4 AM from the deployment team or dev team that a new developer was responsible for, and they either forgot or were unaware.
I remember a particular engineer who was repsonsible for deploying and he had paper checklists, we built him deployment tools that forced him to record his checklist, yet he would always forgot to set the connection string (resulting in the 4 am phone call). Point is it's only worth it if your going to use it vigilantly.
I've found the best way to use this is by implementing your rules into a code analyzer like fxcop.
I think what is more useful than keeping a log of individual mistakes is making sure you come away with a real understanding of why it was a mistake in the first place. Most mistakes stem from a lack of understanding about one thing or another, correct that understanding and you eliminate an entire set of potential mistakes in the future. If I would log anything it would be what I learned from the experience that I didn't know before, not the specifics of the mistake that was made which is likely to be of limited usefulness when you look back at it later.
what was the mistake
how can it be avoided
add the latter to an appropriate checklist, and refer to it as often as appropriate
I think tracking mistakes can be worthwhile, but in my experience it helps a lot to categorize them at some level.
Every programmer is going to make enough mistakes over the course of their career to fill an encyclopaedia. If you make a huge checklist out of all of them then you're never going to get any coding done because you'll eventually be spending all of your time going over your checklist. So: categorize your mistakes in some way that makes sense to you so you can rifle through your list looking at the most important mistakes for the sort of code you're currently working on.
Also, to add to the above as far as what to collect:
what are the symptoms of the mistake (so you can find it later)
how you actually solved it
Yes, tracking your personal mistakes is beneficial. Refer to the SEI for numerous data points (here's one at random). One such methodology is the Personal Software Process (PSP). It's too long to go into here, but here's a book about it. There's also this free SEI publication on PSP.
If you balk at SEI and think Agile is the way to go, you'll probably get better mileage out of a book like Clean Code: A Handbook of Agile Software Craftsmanship (publisher website).
Bottom line: disciplined developer = good, undisciplined developer = bad.
I would also want to ask the question of how much time would be required to accurately track the mistakes, and if that time could be better spent directly on improving the software instead. If you can do this in a minimal amount of time and are able to refer back to your records to prevent future mistakes, it may be valuable. In the long run though, I think it will be better to stick with an absolutely high level list of common mistakes you make.
I'd look for trends or similar types of errors that tend to recur. Once you're aware of the types of errors you make, you can be alert for them, or you can change your coding style to avoid them.
As an example, I worked very closely with my office-mate in a previous life. At one point I was halfway through my first sentence explaining some odd behavior, and he interrupted with, "increment your counter." I still double-check my loop counters today!
Instead of keeping a log of future mistakes, maybe you could review your bugtracker history, and try to find common types of errors which keep ocurring.
I'm so inspired I think I'll try this tommorow.
"What information related to my mistakes should I be keeping track of so that I can improve as a programmer?"
Read other people blogs. Write your own. You don't have to publish it. But you do have to turn each mistake into a story.
Don't drown this in metadata. This isn't amenable to a database or even a bug tracker. A mistake is a story where someone made a bad choice and recovered from it.
Here's your outline.
Context. What was going on.
Situation. The problem you were trying to solve.
What you did. Why it was bad.
What you should have done. Why it was better.
What changed. What you learned.
You should also record your successes in almost the same form.
Context. What was going on.
Situation. The problem you were trying to solve.
What you did. Why it was good.
What you learned.
When in doubt watch a movie. Seriously. Characters a confronted with choices, make mistakes and recover from those mistakes. That story line is the essence of drama. A mistake is the same thing.
Be careful what you log. Not every bad situation is a mistake. Some situations are inevitable or so far outside your control that nothing can be done about it.
Bad planning on someone else's part which puts you into panic-mode isn't your mistake. You can meticulously log everyone else's mistakes, but what's the point? If you're tracking what other people are doing, you should seek therapy.
Bad planning which provides inadequate budget, schedule or communication about changes may be a compound problem.
You didn't provide enough planning information up front.
In the scramble to cope, you made another mistake which lead to problem resolution and recovery.
In hindsight (which is always 20/20) you can see a decision which was wrong. However, at the time, it was based on best available information. That's not a mistake. Any sentence that begins "If we'd known that X..." is useless for mistake analysis. You can try to write a checklist of all the things you need to know next time you make that decision. But next time, you won't be making exactly that decision and some other piece of information will turn up missing.
Never call other people's actions mistakes.
Find the root causes. It's a root cause when it's something you can't control.
Don't retroactively call information that turned up missing a mistake.
I have been thinking the same thing. For the time being I keep a little spreadsheet with bugs I correct. The purpose of this is to make myself aware of the more common errors being made.
Hopefully I will start to see patterns and avoid making common errors over and over again.
I find that this makes my job more interesting, probably since I am an analytical person who likes to collect data and information in a structured manner.
You should try to relate the errors to use of inadequate tools, habits, methods or knowledge. In most cases you will find there would have been ways to catch the error earlier. Things like type-safety, unit-testing, code-review, coding standards, better API design, better documentation and working environment, comes to my mind.
I use JIRA with a custom workflow. For a bug, the last step before an issue is closed is a screen that allows me to select a "root cause". I've got a history of the root cause of every bug that's occurred in the last 3 systems I've built.

How can I think like a user? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
We're neck deep in a project right now, schedules are tight (but reasonable). Our general strategy is to get a strong beta done, release it for testing, and get feedback from our testers.
Quite frequently, we're being hit by small things that spiral into long, time-costing discussions. They all boil down to one thing: While we know what features we need, we are having trouble with the little details, things like 'where should this message go' and 'do they need this feedback immediately, or will it break their flow, so we should hold off'?
These are all things that our testers SHOULD catch, but
a) Each 'low priority' bug like this drains time from critical issues
b) We want to have as strong a product as possible
and
c) Even the best testing group will miss things from time to time.
We use our product, and we know how our users use the old version...but we're all at a loss as to how to think like a user when we try to use the new version (which has significant graphical as well as underlying changes).
edit - a bit more background:
We're writing a web app used by a widely-distributed base of users. Our app is a big part of their jobs, but not the biggest (and, of course, we only matter to them when it doesn't work). Getting actual users in to use our product is difficult, as we're geographically distant from the nearest location that serves as an end user (We're in Ohio, and I think the nearest location we serve is 3+ hours away).
The closest we can get is our Customer Service team (who have been a big help, really) but they don't really think like the users either. They also serve as our testers (it really motivates them to find bugs when they know that any they DON'T find may mean a big upswing in number of calls). We've had three (of about 12 total) customer service reps back here most of the week doing some preliminary testing...they've gotten involved in the discussions as well.
Watching someone using the app is a huge benefit to me. Possibly someone who is not entirely familiar with it.
Seeing how they try to navigate, how they try to enter information or size windows. Things we take for granted after creating/running the app hour after hour, day after day.
Users will always try and do things you never expected and watching them in action might bring to light how you can change something that might have seemed minor, but really makes a big impact on them.
Read Don't make me think.
Speaking generally, you can't. There's not any way you can turn off the "programmer" part of your brain and think like a user.
And you're right about (c), testing groups don't necessarily catch all the bugs. But the best thing you can do is get a testing group comprised of real, honest-to-goodness end users, and value their feedback. Draw further conclusions from their general comments.
If you want to know how your users will see your system, the closest you can get is usability testing with real users. Everything else is just heuristics and experience, and is also subject to error. There's no such thing as a bug-free product, but you should be able to get a "strong" product with usability testing.
Buy a cheap, easy to use video camera and record your testers using the app. Even better, get some people unfamiliar with the app. to use it and video them. It's relatively cheap, and you'd be surprised what it will highlight.
I like policy of "eating your own dog food"("http://en.wikipedia.org/wiki/Eat_one's_own_dog_food). It brings you one step closer, because you become a user, although you might think like one.
Try to use your app when you are very hurry (e.g. you have someone who waits for a dinner).
You will see all this little things because you have to wait, you have to go back to the mouse of the keyboard, etc.
And also, make your wife use it. Or your mother.
Another useful test : help someone to use it, by phone. If he can't find the button with your directions, that's probably a bug.
The important thing is to get enough information that you yourself can become a "user". Once you do that you can answer most questions yourself.
The way I always do this is to go talk with them about what they need to do, what they typically do, and how they use their current tools to do it. Then (very important) sit with them while they do it. Make sure you get on with them well enough that you can come back to them with questions about how they handle edge cases you think of later (often the answer will be the appalling "we go around the system manually for that").
I will almost always notice something they are doing that is a royal PITA that they didn't bring up because they are used to having to do that and don't know any better. I will always notice that their %90 typical workflow isn't the easiest workflow the tools provide.
You can't really rely on plain old-fashioned requirements gathering by itself, because that is asking them to think like a developer. They generally don't know what is possible to do with your software, what is easy, and what is hard. Also they typically have no clue on GUI design principles. If you ask them for design input they will just tell you to put any new control on their favorite page, until the thing looks like a 747 control panel.
The problem is often that even the users don't know what they want until they are actually working with the software. Sometimes, a small oversight can be a big usability problem, sometimes a well thought out function that was requested by many users sees only little use.
My suggestions to decrease the risk of not implementing the right usability features:
Take a look at users actually doing their day to day work. Even if they use another software or no software at all. You will be able to determine the artifacts they often need to get their job done. You will see what data they frequently need. Concentrate on the artifacts, data and workflows most used. They should be the most usable. Exotic workflows may be a bit more time consuming for the users than often used workflows.
Use working prototypes of the GUI to let users work through a realistic workflow. Watch them and note what hinders them and what works well. Adjust your prototypes accordingly.
If an issue arises in an often-used part of your software, it is time to discuss it now and in details. If the issue concerns a seldom used part, make it a low priority issue and discuss it if you have the time. If issues or suggestions are low priority, they should stay low priority. If you can't determine if solution A or solution B is the best, don't run in circles with the same arguments over and over. Just implement one of the solutions and see if the beta testers like it. The worst thing you could do is waste time over tiny issues, while big issues need to be fixed.
A software will never be perfect, because the viewpoints of users differ. Some users will think that a minor problem breaks the whole application. Others will live with even severe usability issues. People tend to lend their ear to those who argue the loudest. Get to know your users to separate the "loud" issues from the important ones. It takes experience to do this, and sometimes you will make wrong decisions, but there is no perfect way, only one of steady improvement.
If you can, set aside a certain amount of usability development resources for the rollout phase of your software. Usability issues will arise when people start working with it in a real production environment. Sometimes it is not important to present the perfect software, but to solve issues quickly as they arise.
The flippant (yet somewhat accurate) answer to how to think like a user is put a knitting needle in your ear and push really hard.
The longer response is that we as programmers are not normal and I mean that in a good way. I scratch my head at the number of people who still run executables they receive from strangers in emails and then wonder how their computer got infected.
Any group of people will in time develop their own jargon, conventions, practices and expectations. As a programmer you will expect different things from an operating system than Joe User will. This is natural, to be expected yet hard to work around.
It's also why BAs (business analysts) exist. They typically come from a business or testing background and don't think like programmers. They are your link to the users.
Really though, you should be talking to your users. There's no poitn debating what users do. Just drag a few in and see what they do.
A usability test group will help.. tests not focused on discovering bugs, but on the learning curve of the new design, made by a group of users, not programmers.
I treat all users like malicious idiots.
Malicious because I assume all users are going to try and break my code, do stuff that is not allowed, avoid typing in valid data, and will do anything in their power to make my life hell.
Idiots because again I can't assume they will understand simple stuff like phone formats, will run away screaming if presented to many choices, and will not make any leap of faith on complicated instructions. The goal is to hold their hand the entire way.
At the same time, its important to make sure the user doesn't realize you think they're an idiot.
To think like a user, be one. But are these actually bugs that your testers are reporting? Or are they "enhancement requests"? If the software behaves as designed per requirements and they just don't like the way it operates, that's not a bug. That's a failure of requirements and design. Make it work, make it rock solid, make it easy to change and you'll be able to make it what your users want.
I see some good suggestions here, especially observing people trying to use you app. One thing I would suggest is to look at the order in which things are presented to the user on paper forms (if they use these to do data entry from) and make the final data entry page mimic that order as closely as possible. So many data entry errors (and loss of data entry speed) are from them having to jump around on the page and losing their place. I did some work for a political campaign this year and in every case, entering data was made much more difficult because the computer screen did things in a differnt order than the paper inputs. This is particularly important if the form is one that can't be changed (like a voter registration form, a campaign has to use what the state provides) to match the computer screen. ALso be consistent from screen to screen if possible. If it is first Name last name on one form, making it last name first name on the next will confuse people and guanteee data entry errors.
If you are truly interested in understanding users though I strongly suggest taking a course in Human factors engineering. It is an enlightening experience.
The 'right' way to do this is to prototype (or mock up) your new interface features, and watch your users try to use them. Nothing is as enlightening as seeing a real user try to use a new feature.
Unfortunately, given most projects time and resources, this is not possible. If that is the position you are in I would recommend you discuss in the team who has the best grasp of usability, and then make them responsible for usability decisions - but that person will need to regularly consult real users to make sure his/her ideas are consistent with what the users want.
I'd suggest doing some form of usability testing; I've participated in such in the past, and found them quite useful.
If you were writing a ticketing system, for example, bring up tasks, and ask questions like "how would you update this ticket" or "what do you expect to happen if this button is clicked".
You don't necessarily need a full application, either, in some places screen shots can be used.
You could take the TDD/BDD approach and get the users involved before beta, having them work with you on refining requirements as you write your unit tests. We're beginning to incorporate some of those trends into our current project, and we're seeing fewer bugs in the areas where we have involved the users earlier.
There is no "think like a user" technique, get your hands on someone who knows nothing of the project and throw what you have done at them.
It's the only way to see how the look + feel + functionality present themselves to the end user.
Once you shocked that person who knew nothing of the product, listen to all of their idiotic (or so you think they are) complaints, fix them, arrange every silly cosmetic thing they point out (either by fixing the UI or by improving whichever documentation you had)..
and after you have satisfied the person you chose to look at your app from zero knowledge on the subject first round, pick another ...and another... until they stop being shocked when they see it, and they don't get stuck on.. "ok.. what does this do?" kind of phases.
You (as a member of the project, be it the project manager, developer, etc) will never think like a user is my answer to that question.
Old saying: You can make something "fool proof" but you can't make it "Damn-fool proof".
Additionally: When you make something "idiot proof" the world invents a better idiot.
Other than that, I agree with what everyone else said.
Ask someone with absolutely no knowledge, insight or programming experience to use the program and try to figure out every function of the program.
People who would NEVER use such a program are most likely to find bugs.
See it as a new Safari user (or FF) who tries to put the URL inside the search field...
As a programmer you guess no-one would be that stupid (or, well.. unknowing), but people actually sometimes find themselves in these situations. As a programmer, we miss these things.