Skip to content

Feed aggregator

Pair Programming: Some thoughts

Mark Needham - 2 hours 36 min ago

Mark Wilden pointed me to a post he's written about his experience pair programming at Pivotal Labs where he makes some interesting although not uncommon observations.

When you pair program, you're effectively joined at the hip with your pair. You can't pair if only one of you is there.

I've previously written wondering what we should do if our pair isn't around where I was leaning more towards the opinion that we should try to continue along the same path that we were on when working with our pair if they're gone for a short amount of time and to find a new pair or work alone if they're gone for longer.

On the projects I've worked on we'll still have times working alone when there's an odd number of people around or if someone just feels like working on their own and I think that's fine as well. I don't think we need to pair 100% of the time.

You have to be able to think out loud – 8 hours a day. Then you have to type in code while someone is watching you. They'll catch your typos (hopefully after giving you a chance to spot them yourself) and they'll see when you're floundering for how to do something.

I find that this is quite a useful practice for explaining things to yourself although I can see how it would initially exhausting.

Even now there are times when I just want to write some code instead of having to explain what I want to do to someone else. Sadly almost every time I explain something it turns out that my pair has a better idea of how to do it than me so I'm always glad pairing encourages this conversation.

Pair programming doesn't encourage quiet reflection and exploration. You can't just sit back and read some code. You can't just sit and think. I mean, you can, but then your pair is just sitting there.

This is a bit of a double edged sword – pair programming does encourage us to get things done but it's also true that sometimes we need to get the whiteboard out.

Often just sketching out the problem on a piece of paper to check your understanding is enough to trigger a conversation which might result in a better solution.

It does tend to need one person to drive this process though. I haven't seen it just happen organically.

We rarely pair 100% of the time so there are often times when you get a bit of time to play around a bit with the code and see whether specific approaches would work out and I often use this time for reflection and exploration.

One thing which a couple of the commenters on the original blog suggested is that perhaps more rotation was needed to help overcome some of the problems and from my experience it's vital that we do rotate otherwise the pair will end up hating each other!

I recently worked on a story with 3 other people across its life and each person pointed out something that I hadn't previously considered and which led to an eventual output that was much better than it would have been otherwise.

I think rotating different people onto a story can help lead to more innovative design as long as we have people working together who are relatively flexible and open to trying out new ideas.

Mark's post is certainly interesting though and helps identify some of the things we need to be aware of when pair programming – we don't just want to follow the practice blindly.

Categories: Programming

Making Ruby Fast: The Rubinius JIT

Engine Yard Blog - 2 hours 40 min ago

In order to execute Ruby code as fast as possible, Rubinius has the ability to compile Ruby code all the way down to machine code when it detects that a method is heavily used. In Rubinius, the system that manages this process is its JIT.

In today’s post, I’ll be giving an overview of the various players involved in the path that code takes to get from source to machine code. Without further ado, I’ll jump right in.

Melbourne Parser

This is the first step. The parser takes Ruby source code as input and calls for each element to create an internal representation of the code: the AST. (lib/ext/melbourne)

Compiler

The compiler takes the AST that the parser created and analyzes it, creating bytecode in the form of a CompiledMethod and InstructionSequence. (lib/compiler)

Bytecode

A CompiledMethod object contains an InstructionSequence object, which is the raw bytecode which will perform the semantic actions of the Ruby source code. (lib/compiler/iseq.rb)

Virtual Machine

The VM itself then executes the bytecode using a simple interpreter. A key data structure used in this evaluation is the VMMethod, which is an internal mirror of a CompiledMethod, but translated into constructs that are easier to interpret. As the VM interprets a VMMethod, it uses InlineCache objects to speed up method dispatch. In addition, these InlineCache objects remember profiling information about what methods they have seen. This information is later used by the JIT.

The VM also increments a call counter on the VMMethod at a few critical points (on start and on backward branch). This call counter is what controls when the JIT kicks in. When the call counter reaches some predetermined value (controlled via -Xjit.call_til_compile), the first stage of the JIT kicks in.

Method Chooser

Now that a call counter has reached the proper level, the JIT is ready to kick in. The JIT could simply take the method whose counter has hit the level, but instead it starts the search. It’s looking for a good method to JIT, which it finds by looking up the call stack.

The reason it does this is because the JIT has the ability to inline methods into methods that call them. We’re exploiting the fact that the call stack shows not just one method heating up, but a whole chain of them (vm/llvm/jit:compile_callframe). So we walk up the call stack, looking at each method along the way, and asking ourselves: could this method be inlined into the one that called it? If the answer is yes, we move to the next method. By doing this, we’re able to inline methods along hot paths in code, which yields better speeds.

Compiler Thread

Now that we’ve picked a good method at which to start the JIT process, the method is placed into a queue. This queue is then automatically emptied by another native thread, which is always running. It’s in this background thread that the rest of the JIT process takes place.

Using a background thread means that the Ruby code is free to continue to run while the JIT runs in the background. This means that the JIT imposes virtually no slowdown, because it never stands in the way of running Ruby code (vm/llvm/jit:compile_soon).

The JIT thread pops an entry off the queue and begins compiling it. Because the JIT uses LLVM to perform low level optimizations and machine code generation, we need to translate the method into a structure that LLVM understands. This structure is the LLVM IR.

To convert the method, we walk through the bytecode and call methods on a JITVisit object (one for each kind of bytecode). The JITVisit class uses LLVM’s IRBuilder class to build a big tree data structure that represents the actions that should be taken.

A simple example is that a goto bytecode instruction is translated into a Branch object and inserted into the IR. For most bytecode, the process is fairly straight forward, there being a simple set of IR objects to generate per bytecode.

Method Inlining

The most complicated bytecodes to generate IR for are the send instructions. This is because it’s at these points that we have the opportunity to inline a method. When a method is inlined, the code to perform the method is inserted where the send instruction would normally be. This eliminates any calling overhead and allows LLVM to optimize more.

At this stage, control is handled to an Inliner object. The Inliner will only inline a method if it can see that the method was the the primary method called at a particular send instruction. Because Ruby is a dynamic language, any method call can always invoke a brand new method. But in reality, that happens rarely. Instead, most method calls always end up calling the same method over and over again. The profiling information that the InlineCaches have been gathering allows us to see that this is the case and perform inlining.

One constraint that the Inliner has is that it needs to avoid over-inlining. Over-inlining causes the generated function to become extremely large and slower than it would be if there were no inlining. To do this, the Inliner keeps track of the cost of a inlining. For every method that is inlined, the cost increases. When the cost reaches a threshold, no more inlining takes place.

Now that the Inliner has decided to go ahead and inline the method, it must insert a guard before the inlined code. This guard makes sure that the object is still of the type seen in the profiling information, and that therefore the inlined method code is the proper code to run. The generated IR for this looks something like:

if(obj->class == profiled_class) {
 result = inlined_code_for_method_name;
} else {
 result = obj->send method_name, …;
}
This allows Ruby code to continue to be dynamic, but exploits those points in the code where the dispatch is actually static.

After the JITVisit class has finished, control is handed off to optimize and generates machine code.

LLVM Optimization

Up to now, we’ve simply been constructing information to feed to LLVM. Now we actually hand over the IR to LLVM. The first thing LLVM does is run a number of optimization passes over the IR. This cleans up the IR and makes it quite a bit more efficient. At this stage, the IR can be reduced in size by five to ten times by remove redundancies and reordering.

LLVM Code Generation

Finally, the optimized IR is run through LLVMs code generator. This code generator is fairly complex, but its API is extremely simple. When the generator finishes, it returns a function pointer that can be called to execute the code. We put this function pointer into a special slot on the original CompiledMethod.

By default, this slot in the object holds a pointer to the function that implements the interpreter. By swapping them, any future calling of the method automatically uses the new JIT’d version.

Deoptimization

Because Ruby is so dynamic, there are cases where JIT’d code must be discarded. The primary example is where a method that was inlined is redefined. In this case, the VM keeps a table to know all methods that inlined the method being redefined. It resets those methods back to using the interpreter and tags the JIT’d code to be discarded. Because the method has been reset, it can now be JIT’d again later, incorporating the newly redefined method.

So those are the systems that interaction to make speed up Ruby. The process can achieve speeds up by as much as 10x over non JIT’d code. We’ve only begun to scratch the surface of the techniques we can use to strip even more dynamic aspects of the code away and make it faster. 2010 is going to be an exciting year.

Categories: Programming

Lovely Review of Manage Your Project Portfolio

Steve Berczuk has a lovely discussion of Manage Your Project Portfolio. You can see his review here.

Post to Twitter Tweet This Post

Categories: Project Management

Communicating with a Tuna Fish Cans and String

Herding Cats - Glen Alleman - 4 hours 34 min ago

Geoff Crane of Paper Cut Project Monitoring described his TwitterView with Jhaymee Wilson. I had not heard of a "TwitterView" before this. It seems to be an interview over Twitter.

I've been interviewed twice this year (2010), once by PMI for an upcoming article and other time for a commercial Blog. Both times were around an hour on the phone and an editorial session after to do the "fact check" and final edits of the interviews.

TunaFishComm What came to mind with Geoff's post  is doing an interview with two tuna fish cans and a string.

We did this as kids. It's half duplex, low fidelity, highly distorted. Fun but not very effective.

Endless studies have shown that communication between people is largely through body language and expressions. Then comes tonality, and finally the words themselves.

Now I enjoy Geoff's Blog very much and the people he points us too as well.

But I still have gotten my mind around the notion of Twitter as a communication channel for anything other than exchanging short - almost context free - messages. 

I live on IM with our field staff. It's a way to see who's in, a quick check up on something that can be resolved in a very few sentences - like ONE. For example - "who's using the WebEx account?" "Hey Matt, I'm headed to 59th facility, you gonna be there for lunch?" "Hannah, you coming to Breck this weekend with your roommates?" "Honey, stop by Safeway and get two bunches of green onions." Stuff like that.

Serious adult communication seems to require a wide channel.

Managing projects requires an even wide channel. No waving, no doodling on the reports, no looking at multiple pieces of paper at the same time, no sensing of the facial expressions to see if we're actually exchanging information rather than just textual characters.

Someone please tell me what am I missing here, where the PM2.0 advocates claim this is the "next big thing," in the domain of Project Management?

Categories: Project Management

Nine Questions to Assess Team Structure

Mike Cohn's Blog - 9 hours 47 min ago

It is perhaps a myth, but an enduring one, that people and their pets resemble one another. The same has been said of products and the teams that build them. If it is true that a product reflects the structure of the team that built it, then an important decision for any Scrum project is how to organize individuals into teams. This paper presents a set of guidelines to consider in designing an appropriate team structure. Each guideline is presented in the form of a question to be asked of a current or proposed team. The questions are intended to be asked iteratively. Ask each question of a current or proposed team, changing the structure as appropriate based on the answer. As the structure changes, re-ask the questions until you can answer “yes” to each.

  1. Does the structure accentuate the strengths, shore up the weaknesses, and support the motivations of the team members? People don’t enjoy being on a team where they are not able to make use of their strengths or are constantly required to do things they are bad at. Good team members are willing to do whatever is necessary for the success of the project, but that doesn’t relieve us from the goal of trying to find a team structure that accentuates the strengths of as many team members as possible.
  2. Does the structure minimize the number of people required to be on two teams (and avoid having anyone on three)? A well-conceived team structure for an organization that is not attempting to do too many concurrent projects will reduce multitasking to a tolerable level. If more than 20% of all team members belong to more than one team, consider an alternative team design or deferring some projects.
  3. Does the structure maximize the amount of time that teams will remain together?
    If other factors are equal, you should favor a design that allows team membership to persist over a longer period. It takes time for individuals to learn to work well together. Amortize the cost of that learning over a longer period by trying to leave teams together as long as possible.
  4. Are component teams used only in limited and easily justifiable cases? Most teams should be created around the end-to-end delivery of working features. In some cases, it is acceptable to have a component team developing reusable user interface components, providing access to a database, or similar functionality. But these should be exceptions.
  5. Will you be able to feed most teams with two pizzas? Given the compelling productivity and quality advantages of small teams, the majority of teams in a good design should have five to nine members.
  6. Does the structure minimize the number of communication paths between teams? A poor team structure design will result in a seemingly infinite number of communication paths between teams. Teams will find themselves unable to complete any work without coordinating first with too many other teams. Some inter-team coordination will always be required. But, if a team that wants to add a new field on a form is required to coordinate that effort with three other teams, as I’ve seen, then the communication overhead is too high.
  7. Does the structure encourage teams to communicate who wouldn’t otherwise do so? Some teams will just naturally communicate with each other. An effective team design encourages communication among teams or individuals who should communicate but may not do so on their own accord. In fact, one valid reason to put someone on two teams is that doing so will increase the communication between those teams. If lack of communication between two teams is a concern, splitting a person’s time between those two teams is easily justified.
  8. Does the design support a clear understanding of accountability? A well-designed team structure will reinforce the concept of a shared, all-teams accountability for the overall success of the project while providing each team with clear indicators of their unique accountabilities.
  9. Did team members have input into the design of the team? During the early stages of your transition to Scrum, this may not be possible. Individuals may not yet have enough experience delivering working, tested, ready-to-use products by the end of each sprint. Similarly, some individuals may be initially too resistant to Scrum to contribute to team structure discussions in constructive ways. In these cases, it is acceptable for managers outside the team to design an initial team structure.
  10. An effective team structure is one of the most critical factors in the success of any agile endeavor. Poorly structured teams will lead to inefficient teamwork, excessive integration challenges, multitasking, low morale and other problems. By using these nine questions to carefully consider how teams are organized you can avoid these problems.

Sponsored Post: Job Openings - Squarespace

Squarespace Looking for Full-time Scaling Expert

Interested in helping a cutting-edge, high-growth startup scale? Squarespace, which was profiled here last year in Squarespace Architecture - A Grid Handles Hundreds of Millions of Requests a Month and also hosts this blog, is currently in the market for a crack scalability engineer to help build out its cloud infrastructure. Squarespace is very excited about finding a full-time scaling expert.

Interested applicants should go to http://www.squarespace.com/jobs-software-engineer for more information.



If you would like to advertise your critical, hard to fill job opeinings on HighScalability, please contact us and we'll get it setup for you.

Categories: Architecture

Applications as Virtual States

This is an excerpt from my article Building Super Scalable Systems: Blade Runner Meets Autonomic Computing in the Ambient Cloud.

As I was writing an article on the architecture of the Storm Botnet, I couldn't help but notice the deep similarity of how Storm works and changes we're seeing in the evolution of political systems. In particular, the rise of the virtual-state. As crazy as this may sound, I think this is also the direction applications will need follow to survive in a complex world of billions of compute devices.

You may have already heard of virtual corporations. Virtual corporations are companies with limited office space, a distributed workforce, and production facilities located wherever it is profitable to locate them. The idea is to stay lean and compete using the rapid development and introduction of new products into high value-added markets. If you spot a market opportunity with a small time window, building your own factories and hiring and engineering team simply isn't an option. Building factories is a bit old fashioned and is left to the select few. These days you get an idea for a product and contract out everything else you possibly can. It doesn't really matter where you are located or where any of your partners are located. If part of your product requires a specialized microprocessor, for example, you'll contract out the R&D and the design. The manufacture will be contracted out to a virtual fab, then the chip will be sent to a contract manufacturing service for integration. Look ma, no hands.

Futurists say land doesn't matter anymore. Nations don't matter anymore. Entire relationships are abstractly represented by flows of money, contracts, information, and products between all these different agents. Interestingly enough, what technology is the absolute master of managing flows? Applications! But we are getting ahead of ourselves here.

Categories: Architecture

Involvement versus Focus – Re-edited

Note:  This essay was originally blogged in June 2009.  It has been re-edited and so here it is  . . .

Involvement Versus Focus; That Is The Question
Thomas M. Cagley Jr

The American Heritage Dictionary defines involvement as “the act of sharing in the activities of a group”.  At its core, I would suggest the concept boils down to collaboration between involved parties to achieve a common goal.  I think we can all agree upon the ideal of using collaborative groups to conquer many types of projects.  Workers acting in a concerted manner in real life leads to pressure on several organizational fronts in order to achieve this ideal.  In a future essay we will explore another of my simple checklists (see www.tcagley.wordpress.com) to implementing involvement but in this essay I would like to discuss the impact of a common goal.  In many organizations the management structure in use is the command and control methodology.  In this method decisions are made at the top of the management pyramid then get transmitted to line management and then to project teams at the epicenter of work.  In this model, management knows best because of experience, advanced degrees, having gone to a seminar or just divine right.  To those being affected it doesn’t matter.

Most process improvement programs extol the virtue of focusing on the needs of the organization or a specific class of users (usually senior or middle management).  All said and done the virtue of focus is important but it is no longer sufficient to make change actually happen.  The “organization” is being taken over by the World of Warcraft generation.  This new crowd is beginning to believe they need to be involved if they are going to be asked to change how they are going to work.  An example of that new expectation of involvement is reflected in the agile movement.  Teams control how they do work and to an extent the work they will accept into the sprint.  To the new generation, focus without involvement has come to mean big brother.  Telling this new generation what to do whether you have their best interest in mind is deemed a paternalistic action, and for those with an expectation of being involved in their future, paternalism is at best grating and perhaps degrading.  What does involvement mean?  What is the new requirement to help an organization mature in terms of discipline and capability?

The collaborative model suggests a different model in which management sets forth the goal then engages all parties to chart the course forward.  Would the classic waterfall implementation of the CMMI survive a collaborative approach or would a more interesting approach to the discipline of software engineering emerge?  I suggest that in combination the structure of the CMMI and the collaborative nature of AGILE methods are the natural outcome in today’s methodological environment.  Note, I would suggest the pressure of the economy would add the philosophy of lean to the mix yielding a three legged stool of the CMMI as a framework and Agile and lean philosophies as the implementation techniques.

If you step away from the model of change without involvement to embrace a more collaborative approach, a better solution will be generated, a solution yielding discipline, collaboration, effectiveness and efficiency.  A solution that yields discipline, collaboration, effectiveness and efficiency.  Focus is a good thing but consider focusing on involvement rather than on using focus as a tool to speak for others.


Categories: Process Management

Developing and deploying Java on middleware and in the cloud: rise of the Virtual Appliance?

Xebia Blog - 16 hours 9 min ago
From Java EE to Google App Engine to GigaSpaces, the idea of developing against a middleware or "infrastructure" API is well established in the Java world. But these are fixed environments. With the (re-)advent of virtualization, it is now becoming feasible to package and rapidly provision your own environment, custom-designed to meet your application's needs. As the [...]

Leading Lean Software Development – Results are not the Point

From the Editor of Methods & Tools - 17 hours 43 min ago

What fascinates me the most in the Lean software development approach is the quality of the people that support it. The Poppendieck are not an exception to this rule. Their book achieves the seemingly contradictory goals of being very insightful but still easy and captivating to read. It might be however easier to have the right flow when you are a Lean adept ;o)

The book starts with a chapter on systems thinking that takes also examples outside the software development world like Southwest Airlines. The next chapter on technical excellence is dedicated to a panorama of the software development approaches. Chapter 3 is kind of my favorite part of the book, extracting process management knowledge from the history of the construction of the Empire State Building, a project that took only one year to be completed. Chapter four presents the tools for improvement. Finally, the last part of the book is dedicated the people and leadership aspects of Lean.

The structure of the book makes it very pleasant to read, mixing the presentation of lean concepts with case studies and short personal stories. It is definitively a book that I will recommend to every software developer and manager…. and wish that every software developer and manager had read. Even if you think that Lean is not for you or you are a Toyota owner, this book provides a mind-opening text about what the values of software development and organizations should be.

Reference: “Leading Lean Software Development – Results are not the Point”, Mary and Tom Poppendieck, Addison-Wesley, 278 pages, IBSN 978-0-321-62070-5

Get more details on this book or buy it on amazon.com
Get more details on this book or buy it on amazon.co.uk

The Kanban vs Scrum argument stinks! But, can we learn anything from it?

There were some interesting conversations on twitter last evening, so interesting that they are worth some comment.

There was an interesting back-and-forth between @jurgenappelo and @agilemanager about whether it is possible to do Scrum in a "pull" mode or not as well as other issues. But this was just the tip of the iceberg of the Kanban vs Scrum discussion. Here's one comment that caught my eye:



Here @agilemanager is trying to prove that Scrum cannot be done "right" because of inherent problems, specifically that velocity is so unstable that it cannot be used for planning. That argument, however, is easily proven wrong.
Here is a graph of a team implementing scrum, using velocity to plan for success and with a rather stable velocity. (Technically the velocity is "under control" as defined in statistical process control).



This tells us that it is possible (unlike @agilemanager states) that using Scrum planning based on velocity is possible. And in fact that's the main long term planning metric that I've used in the past with success.
The fact that we have a (statistically) controlled velocity allows us also to do other things like identify common causes and special causes that will require different action/intervention with the team in order to improve their overall result.

Then @jgoodsen pitched in with this comment:



Here @jgoodsen is disagreeing with @jurgenappelo's statement that as Kanban gets wider adoption it too will be mis-applied and lead to failure. This type of extreme position taking is an example of why the discussion between Scrum-mendalists and Kanban-istas is quite useless for the rest of us that are interested in learning more.

The point is: Kanban, just like Scrum, initially is being practiced by early adopters. People that tend to read and study things more and earlier. These are also typically people that are given a license to experiment, to try out new things.
In the end the effect is that, typically, early adopters are better at adopting new methods because they have done that more often (they are early adopters after all). Kanban is just another method/framework/whatever, it will succeed and fail as much as Scrum does. @jurgenappelo is right, any method fails when applied by a population large enough.

The takeaway from this discussion for us should be that methods, ultimately, are irrelevant. It is the learning achieved by experimenting that matters.

We should be sharing our learning, not our method allegiance!

Some Day Kanban Will Fail 75% of the Time

NOOP.NL - Jurgen Appelo - Tue, 03/09/2010 - 06:44

Ship Yesterday I had a bit of an argument on Twitter about differences between Scrum and Kanban. Personally I don't care which is better than the other, because I believe that all models are wrong, but some are useful. And both Scrum and Kanban can be useful, given a certain context.

In yesterday's keynote speech at the Scrum Gathering in Orlando Jeff Sutherland said he had seen teams that were "doing Scrum" while they didn't even have a backlog. And there are reports of "Scrum teams" not practicing daily standup meetings, and teams not delivering a new product release every week.

These are not Scrum teams. They are ScrumBut teams. They do Scrum, *but* without some of the key ingredients.

Unfortunately, some people arguing against Scrum include these ScrumBut teams in their evaluations of the "high failure rate" of Scrum. They love quoting that "at least 75 percent of Scrum implementations fail." And I think "Yes of course, 75% fails when that includes the teams that don't understand what they're doing."

I believe that right now Kanban doesn't suffer from this problem. Kanban doesn't have a high failure rate because Kanban is still at the start of its adoption curve. Only very smart people like David Anderson and Karl Scotland are practicing it. And they know how to do it right!

But just wait a few years and see. When idiots like me get their hands on Kanban, we will start implementing KanbanButs, but we'll be calling it Kanban. We will have absolutely no idea what we're doing, because value stream mapping is not as simple as story point estimation. And we will introduce "Kanban teams" without limited WIP, or "Kanban teams" without a vizualized workflow.

Then the world will see a 75% failure rate of "Kanban" implementations.

And then there will be a great new software development method called Bonkiborki (which is the Mongolean word for 42). And I will have invented it. And it will have a much higher success rate than Scrum and Kanban, because I will be the only one who knows how to do it right.

(image by Misserion)

Twitter Twitter - Rss Subscribe - Email Newsletter - LinkedIn LinkedIn - SlideShare SlideShare

tweetmeme_url = 'http://www.noop.nl'; tweetmeme_source = 'jurgenappelo';
Latest, greatest and favoritest posts:
Yes, Good Managers Are Manipulators
Reduce Your Fear, Increase Your Status
Planning for Feature-Complete Deadlines


Categories: Project Management

Getting real: Book review

Mark Needham - Mon, 03/08/2010 - 22:56

I recently came across 37 Signals 'Getting Real' book where they go through their approach to building web applications and there have certainly been some good reminders and ideas on the best way to do this.

These are some of my favourite parts:

  • Ship it!

    If there are minor bugs, ship it as soon you have the core scenarios nailed and ship the bug fixes to web gradually after that. The faster you get the user feedback the better.

    Often on projects I've worked on we've taken the approach that bugs get worked on before new stories which makes sense in a way because it means that we are fixing problems quickly and keeping the quality of the application high.

    In reality what often happens is that low priority bugs just end up not getting looked at but I like the fact that we can choose to make that an explicit approach rather than just allowing it to happen to us.

    Prioritize your bugs (and even ignore some of them)

    Just because you discover a bug in your product, doesn’t mean it’s time to panic. All software has bugs – it’s just a fact of life.

    I find it interesting that there might be more value in getting something out the door and then getting feedback on it rather than spending extra time perfecting it up front.

  • Fix Time and Budget, Flex Scope

    You have to figure out what’s really important. What’s going to make it into this initial release? This forces a constraint on you which will push you to make tough decisions instead of hemming and hawing.

    From my experience a lot of times we end up implementing features just because that's what was agreed in the initial release plan and there is often a reluctance to change that even if a feature isn't really that useful anymore.

    It becomes even more problematic if we get to the stage where it's not possible to deliver all the features promised in the remaining time so it certainly makes sense to me that in that situation we would look to focus on getting the absolutely essential things in first.

  • Choose any enemy

    Sometimes the best way to know what your app should be is to know what it shouldn’t be. Figure out your app’s enemy and you’ll shine a light on where you need to go.

    This seems to be a much better idea than just copying the ideas of your competitor which might seem the obvious thing to do if you're working in the same area.

    The problem with that approach of course is that when you do copy you have no actual vision of what you're doing with your application anyway so you'll always be playing catch up.

  • Don't overcomplicate the application

    There are a few parts of the book where the authors talk about keeping the application simple and then letting the users play with it:

    The harder we tighten things down, the less room there is for a creative, emergent solution. Whether it’s locking down requirements before they are well understood or prematurely optimizing code, or inventing complex navigation and workflow scenarios before letting end users play with the system, the result is the same: an overly complicated, stupid system instead of a clean, elegant system that harnesses emergence.

    Keep it small. Keep it simple. Let it happen.

    Andrew Hunt, The Pragmatic Programmers

    The users can then decide for us where we need to fill in more details:

    Details reveal themselves as you use what you’re building. You’ll see what needs more attention. You’ll feel what’s missing. You’ll know which potholes to pave over because you’ll keep hitting them. That’s when you need to pay attention, not sooner.

    In particular they suggest that focusing on very specific details about the page layout/colour/wording can be left until later because it will only serve to hinder forward progress if we concentrate on it too early.

  • Scaling an application

    You don’t have a scaling problem yet

    “Will my app scale when millions of people start using it?”

    Ya know what? Wait until that actually happens.

    On several projects that I've worked on there often seems to be a desire to focus on performance and scaling an application very early on which seems wasteful when we could be focusing on actually building something that has so many users that we need to scale it later on. I think this advice is spot on.

  • Write less software

    A common theme throughout the book is that of writing less software to achieve our goals:

    The best designers and the best programmers…are the ones that can determine what just doesn’t matter.

    That’s where the real gains are made.

    Most of the time you spend is wasted on things that just don’t matter. If you can cut out the work and thinking that just don’t matter, you’ll achieve productivity you’ve never imagined.

    Innovation is not about saying yes to everything. It’s about saying NO to all but the most crucial features.

    Throw away customer feature requests – if they're really important then they'll come back anyway

    Don’t worry about tracking and saving each request that comes in. Let your customers be your memory. If it’s really worth remembering, they’ll remind you until you can’t forget.

    The authors ideas around preferences were particularly interesting to me:

    Preferences are also evil because they create more software.

    More options require more code. And there’s all the extra testing and designing you need to do too.

    I hadn't appreciated until recently quite how much complexity we can add to an application by allowing users to play around with the display of information on a screen.

    It seems like a nice feature to have but it would be interesting to see statistics that could tell us what percentage of users actually that type of thing when it's not the core idea of the application.

    I also quite liked the following and I think it's something that we need to do more often on teams:

    Encourage programmers to make counteroffers.You want to hear: “The way you suggested will take 12 hours. But there’s a way I can do it that will only take one hour. It won’t do x but it will do y.” Let the software push back. Tell programmers to fight for what they think is the best way.

  • Decisions are temporary so make the call and move on

    So don’t do the “paralysis through analysis” thing. That only slows progress and saps morale.

    Instead, value the importance of moving on and moving forward. Get in the rhythm of making decisions. Make a quick, simple call and then go back and change that decision if it doesn’t work out.

    I think a big part of this is getting the mentality that it's fine to make changes after we've 'finished' something. Any other approach doesn't work from my experience.

  • Reduce meetings

    Meetings usually arise when a concept isn’t clear enough. Instead of resorting to a meeting, try to simplify the concept so you can discuss it quickly via email or im or Campfire.

    I find it interesting that they prefer communicating by email because I've often found that it's not the best communication mechanism since it's really easy to misinterpret what people mean.

    Having said that if we can make concepts clearer and the need for a meeting is an indicator that we need to do that then perhaps we can still meet in person and just make the meeting much shorter.

  • Design the interface before you start programming

    Too many apps start with a program-first mentality. That’s a bad idea. Programming is the heaviest component of building an app, meaning it’s the most expensive and hardest to change. Instead, start by designing first.

    I've certainly fallen into this trap a lot but I've been trying to follow the outside in approach more strictly recently and so far I'm finding that it reduces the feedback cycle quite substantially which is only a good thing.

  • Design for regular, blank, and error states

    For each screen, you need to consider three possible states:

    Regular
    The screen people see when everything’s working fine and your app is flush with data.

    Blank
    The screen people see when using the app for the first time, before data is entered.

    Error
    The screen people see when something goes wrong.

    I'd never even though of this at all and I'm certainly guilty of only ever considering applications when all the data is filled in so this is certainly something else to consider.

  • Tear down the walls between support and development

    In the restaurant business, there’s a world of difference between those working in the kitchen and those out front who deal with customers. It’s important for both sides to understand and empathize with the other. That’s why cooking schools and restaurants will often have chefs work out front as waiters so the kitchen staff can interact with customers and see what it’s actually like on the front lines.

    My colleague Chris Read and some others seem to be trying to close this gap with the devops movement which also has a track at QCon London this week.

    The idea of working in support to see what an application is like from that perspective is something that more experienced colleagues often recommend although I've not done it as yet.

Overall I found this book a really interesting and quick read and although many of the ideas suggested seem like common sense it's strange that we often don't do all of them.

The 37 Signals guys also have a new book coming out in the UK tomorrow titled 'Rework' which sounds like it could be quite a good read as well.

Categories: Programming

Open Source Projects - Apply for Google Summer of Code

Google Code Blog - Mon, 03/08/2010 - 21:08
Google Summer of CodeTM, our flagship program to introduce college students to open source development, opens today. Over the past five years, we've seen more than 3,400 successful students "graduate" from the program, and we're looking forward to welcoming another group of students for our sixth year. We're now accepting applications from open source projects who wish to act as mentoring organizations and will begin accepting applications from students on March 29th. For more details, check out the Google Open Source Blog.

By Leslie Hawthorn, Open Source Team
Categories: Programming

Android NDK r3

Android Developers Blog - Mon, 03/08/2010 - 20:27

The third release of the Android Native Development Kit (NDK) is now available for download from the Android developer site.

It can be used to target devices running Android 1.5 and higher. In addition to a few bug fixes and improvements, this release includes the following new features:

Toolchain improvement

The toolchain binaries have been refreshed for this release with GCC 4.4.0, which should generate slightly more compact and efficient machine code than the previous one (4.2.1).

Note that the GCC 4.4.0 C++ frontend is more pedantic, and may refuse to compile certain rare and invalid template declarations that were accepted by 4.2.1. To alleviate the problem, this NDK still provides the 4.2.1 binaries, which can optionally be used to build your machine code.

OpenGL ES 2.0 support

Applications targeting Android 2.0 (API level 5) or higher can now directly access OpenGL ES 2.0 features. This brings the ability to control graphics rendering through vertex and fragment shader programs, using the GLSL shading language.

A new trivial sample, named "hello-gl2", demonstrates how to render a simple triangle using both shader types.

Name simplification

This NDK release is just called "r3", for "Revision 3", to indicate that it is not limited to a specific Android platform/API level. Some developers thought that the previous release's name (1.6_r1) was confusing and indicated that it could only be used to target Android 1.6, which was not true.

Enjoy!

Categories: Programming

The Business of Code, The Code of Business

Google Code Blog - Mon, 03/08/2010 - 19:17
This post is part of the Who's @ Google I/O, a series of blog posts that gives a closer look at developers who'll be speaking or demoing at Google I/O. This post is written by Albert Wenger, partner at Union Square Ventures (and still enjoys writing code!). Albert will be speaking alongside others in venture capital on a panel at Google I/O.

Reading the Google Code blog, it is hard not to marvel at the fundamental transformation that is taking place in the business of code. By the business of code, I mean the economics of developing and selling software. My first exposure to the software business was as a teenager in Germany some twenty five years ago. Driver's education there is quite expensive because one has to take many mandatory lessons. After all, once you have passed you get to drive on the Autobahn, which, to this date, has long stretches without a speed limit! I thought I was being clever by agreeing to write software for a driving school in exchange for free lessons. It turns out the clever one was the owner of the driving school who turned around and sold the software to several other schools.

So what would it have taken for me then to become an ISV (independent software vendor), other than actually having the idea? These were the early days of PCs. I would have had to spend a lot of money on marketing and a lot of time on in-person sales and on-premise / telephone support. Most ISVs at the time grew locally for that reason and it was not uncommon to have highly fragmented markets with literally dozens of different vendors. As the software would have grown past the very simple initial functionality that I had created, I would have had to write pretty much everything I needed myself. Need billing? Write a billing module. Need asset tracking? Write an asset tracking module. The marketplace for components evolved only much later and was almost as fragmented as the ISV market.

This situation persisted until quite recently. In fact, in 2003 I spent a year getting to know the market for software for trucking companies in the US (Why? That's a long story). That market still had essentially the same characteristics: highly fragmented, regional customer bases, and almost 100% monolithic custom systems.

Since then, the situation has changed dramatically. Today, creating new software means focusing on what the unique contribution is that one wants to offer and figuring out how to integrate with everything else. Need a spreadsheet? Use Google Apps. Need telephony? Use Twilio (disclosure: Twilio is a USV portfolio company). Marketing can happen on the web through keyword advertising and, better yet, SEO and customer sharing. For many solutions, even sales can be entirely web-based (enter a credit card!). Support can happen over the web and often users can support each other through community. Add cloud computing to the mix and you eliminate the fixed cost that was such a high barrier to entry in the early days of the web (I remember the extraordinary bills for servers and bandwidth in 1999!).

All of this has made it possible for small teams to create big successes. It is amazing what a few great coders can do, leveraging all the services that are now available. It is,however, not just the cost side of the business of code that has changed dramatically. Competition has gone global. Someone in a faraway place can create a system, and it is instantly available everywhere. The days of the profitable, regional ISV business are over. The source of competitive advantage has also shifted. In the past, if your solution had better features, you could land a sale even against a competitor that had more customers. Now, better features don't mean that much if the larger competitor has built a network effect into their business. Imagine trying to start a LinkedIn or Salesforce competitor with better features. So the very same forces that are making it much easier to get started are making it much harder to build a successful and sustainable business.

Does that mean that there will be fewer opportunities going forward? Maybe. But there is a strong countervailing force that is creating important new opportunities: the code of business is also changing. By the code of business, I mean how companies and industries (and even societies) are organized. At Union Square Ventures, we are convinced that over time, the Internet will transform most, if not all, industries as much as it is changing the software business. This has started with the media industry, where after years of prediction of change we are now seeing massive shifts.

The reason that the code of business will change is that much of it is based on historic constraints on the bandwidth and latency of information flows. For instance, a command-and-control type hierarchy is still at the heart of (almost) all large corporations. Information flows up the hierarchy with middle management in charge of aggregating information flows. Commands then flow down with middle management translating into finer grained actions. This basic structure dates back to a time of messengers and telegraphs. Corporations are slowly shifting away towards more of an Internet architecture of "small pieces, loosely joined" -- but in many cases the end state may mean that the "pieces" are independent, small companies instead of units of a large company.

These changes will take a great deal of time (decades) because existing structures have a ton of inertia. Far more people tend to be interested in preserving the status quo than in making radical changes. Also, when a whole system needs changing, it is often difficult to get there one piece-at-a-time because all the components need to fit together. But as they start to occur in other industries, the opportunities will be massive. To give just one example, consider education: the size of the textbook industry alone in the US is estimated at $7 billion annually. This is a pure content business ripe for disruption.

Enough reading -- time for everyone with a transformative idea to start coding!

By Albert Wenger, Union Square Ventures and Continuations.
Categories: Programming

Android at the Game Developer's Conference

Android Developers Blog - Mon, 03/08/2010 - 18:26

Tuesday, March 9 marks the start of the 2010 Game Developers Conference in San Francisco, and Android will be there! There has been a lot of interest about Android from the game development community, and our presence at GDC is intended to provide developers everything they need to get started with the platform. We are hosting several technical sessions and participating in two industry panels.

We also want to meet you and answer your questions about Android game development, so we've set aside time for "office hours." Android team engineers will be on-hand to answer your questions, and if you have a game in development for Android, we'd love to see a demo.

Below, you can see the technical sessions that we're hosting and industry panels that we're participating in. We look forward to seeing you at GDC2010!

Technical sessionsTuesday, March 9

Bootstrapping Games on Android
Chris Pruett
Everything you need to know about games on Android in 60 minutes.
1:45 PM - 2:45 PM
Room 309, South Hall

Wednesday, March 10

Bring Your Games to Android
Jack Palevich
An in-depth look at writing and porting C++ games using the NDK and a thin Java shell.
10:30 AM - 11:30 AM
Room 302, South Hall

Get the Most out of Android Media APIs
Dave Sparks & Jason Sams
Tips and tricks for optimizing your sound, video, and graphics for compatibility, efficiency, and battery life.
11:45 AM - 12:45 PM
Room 302, South Hall

Android Office Hours
The Android team
Come meet the team, ask us your questions, and show off your games!
3:00 PM - 4:00 PM
Room 302, South Hall

Industry panelsWednesday, March 10

GamesBeat2010: A sea of mobile devices
Eric Chu
Industry experts weigh in on the future of mobile game development.
4:30 PM - 5:30 PM
Moscone Convention Center

Thursday, March 11

After the iPhone...what?
Dave Sparks
Audio experts discuss the nitty gritty technical details of alternative gaming platforms.
10:30 AM - 11:30 AM
Room 112, North Hall

Categories: Programming

Learn Programming with Microsoft Small Basic

Code Monkeyism - Stephan Schmidt - Mon, 03/08/2010 - 17:35
I’m not very enthusiastic about Microsoft, mostly because they have been a copy cat for decades. But before Microsoft started with Windows, they’ve produced programming languages and were respected by many. I came into contact with Microsoft when playing with AmigaBasic which was build by Microsoft. Understanding BASIC on a VC20 was hard, I had [...]
Categories: Programming

Using Copy-On-Write In Multithreaded Code To Reduce Locking Overhead

I recently posted some code that i asked you to review.  When i posted it, the code had never even executed (that’s right, not even through a test) and i only thought it would do what i needed it to do.  I consider the actual implementation non-obvious (at least for those who don’t know the copy-on-write approach to avoid traditional locking) so i just wanted to hear some reactions to the code from people who didn’t knew the context.  I promised to do a follow-up post to discuss the code in its entirety so here it is.

First, i’ll show the whole class again:

    public class TenantSessionFactoryManager : ITenantSessionFactoryManager

    {

        private readonly ITenantContext tenantContext;

        private readonly ITenantInfoHolder tenantInfoHolder;

        private readonly string mappingAssemblyName;

 

        private readonly object writeLock = new object();

        private Dictionary<Guid, ISessionFactory> sessionFactories;

 

        public TenantSessionFactoryManager(ITenantContext tenantContext, ITenantInfoHolder tenantInfoHolder, string mappingAssemblyName)

        {

            this.tenantContext = tenantContext;

            this.tenantInfoHolder = tenantInfoHolder;

            this.mappingAssemblyName = mappingAssemblyName;

            sessionFactories = new Dictionary<Guid, ISessionFactory>();

        }

 

        public ISession CreateSessionForCurrentTenant()

        {

            var tenantId = tenantContext.CurrentTenantId;

 

            if (!sessionFactories.ContainsKey(tenantId))

            {

                CreateSessionFactoryForCurrentTenant();

            }

 

            return sessionFactories[tenantId].OpenSession();

        }

 

        private void CreateSessionFactoryForCurrentTenant()

        {

            lock (writeLock)

            {

                var tenantId = tenantContext.CurrentTenantId;

 

                if (!sessionFactories.ContainsKey(tenantId))

                {

                    var connectionString = tenantInfoHolder.GetDatabaseConnectionString(tenantId);

 

                    var sessionFactory = new Configuration()

                        .Configure()

                        .AddProperties(new Dictionary<string, string>

                                {

                                    { "connection.connection_string", connectionString },

                                    { "cache.region_prefix", "Tenant_" + tenantId }

                                })

                        .AddAssembly(mappingAssemblyName)

                        .BuildSessionFactory();

 

                    var newDictionary = new Dictionary<Guid, ISessionFactory>(sessionFactories);

                    newDictionary[tenantId] = sessionFactory;

                    sessionFactories = newDictionary;

                }

            }

        }

 

        public void RemoveSessionFactoryForTenant(Guid tenantId)

        {

            if (!sessionFactories.ContainsKey(tenantId))

            {

                return;

            }

 

            lock (writeLock)

            {

                if (!sessionFactories.ContainsKey(tenantId))

                {

                    return;

                }

 

                var sessionFactory = sessionFactories[tenantId];

                var newDictionary = new Dictionary<Guid, ISessionFactory>(sessionFactories);

                newDictionary.Remove(tenantId);

                sessionFactories = newDictionary;

 

                sessionFactory.Dispose();

            }

        }

    }

 

Basically, the purpose of this class is to hold a set of ISessionFactory instances, each of which belongs to a particular tenant in a multi-tenant application.  Tenants can be added on the fly (without restarting the application) and when an ISessionFactory doesn’t exist yet for a particular tenant, it must be created when the first request for an ISession for that tenant comes in.  Obviously, access to the sessionFactories dictionary must be thread-safe since multiple threads will be reading from the dictionary as well as occasionally writing to it.

I considered 3 options to make sure access to the dictionary would be thread-safe:

  1. Traditional locking (through the lock statement or the Monitor class)
  2. Using the ReadWriterLockSlim class
  3. Using the copy-on-write pattern

Traditional locking was quickly scratched from the list because that would require me to lock for every read of the dictionary as well as every write.  Now, pretty much every single request requires an NHibernate session which means that pretty much every single request results in a lookup in the sessionFactories dictionary.  If i need to lock for every read, this significantly hurts overall throughput of the system. 

The ReadWriterLockSlim might be a good solution here… after all, the short description of this class in MSDN says this:

Represents a lock that is used to manage access to a resource, allowing multiple threads for reading or exclusive access for writing.

Sounds like what i need, right?  But the thing is, i’ve never used the ReadWriterLockSlim class before and it hasn’t really gained my trust yet.  I know that’s a terrible excuse for not using it, but here me out.  While the ReadWriterLockSlim likely reduces locking overhead over traditional locking substantially, there still has to be some overhead for read operations, even if it is small.  In most situations, that small overhead wouldn’t bother me but in this case, that little overhead would be added to pretty much every single request in the system.  Now, writing to a dictionary implies that a new tenant has been added to the system.  In the context of this system, that’s not even gonna happen on a daily basis.  Hell, once a week is probably a best-case estimation and even that is highly optimistic.  So i really don’t want any kind of overhead on read operations when the write operation is only going to happen very occasionally.

That leaves the copy-on-write pattern.  I’ve used it before with success (though at the time, i didn’t know it was a known pattern) so this approach has already gained my trust.  It basically implies that we don’t do any locking on the read operations, but whenever a write operation occurs we copy the original set of objects, perform the write on the newly copied set and then set the reference of the original set to the newly created and modified instance.  During this whole time, every single read is safe.  Successive reads within the same logical operation however aren’t, so the following code would not be thread-safe:

            if (sessionFactories.ContainsKey(tenantId))

            {

                return sessionFactories[tenantId].OpenSession();

            }

 

Because there’s no locking on the reads, the code within the if-block could fail because the sessionFactories reference could be pointing to a new dictionary which no longer contains the element for that key. 

Of course, if you have frequent writes, the overhead of copying the set of objects every time you need to add/remove one might be bigger than you want, so this isn’t a pattern that you should use whenever you need to protect access to a shared resource. For this situation however, i think it’s ideal… though i’d obviously like to hear about better solutions :)

Now, let’s take a closer look at the pieces of code that perform the write operations.  First, adding a new ISessionFactory to the dictionary:

        private void CreateSessionFactoryForCurrentTenant()

        {

            lock (writeLock)

            {

                var tenantId = tenantContext.CurrentTenantId;

 

                if (!sessionFactories.ContainsKey(tenantId))

                {

                    var connectionString = tenantInfoHolder.GetDatabaseConnectionString(tenantId);

 

                    var sessionFactory = new Configuration()

                        .Configure()

                        .AddProperties(new Dictionary<string, string>

                                {

                                    { "connection.connection_string", connectionString },

                                    { "cache.region_prefix", "Tenant_" + tenantId }

                                })

                        .AddAssembly(mappingAssemblyName)

                        .BuildSessionFactory();

 

                    var newDictionary = new Dictionary<Guid, ISessionFactory>(sessionFactories);

                    newDictionary[tenantId] = sessionFactory;

                    sessionFactories = newDictionary;

                }

            }

        }

 

As you can see, the entire operation is put between a lock on the writeLock object instance.  The downside of this is that creating an ISessionFactory instance is an expensive operation, which means the lock will be held for a long time (could easily be one or more seconds).  Then again, i don’t anticipate this happening frequently so it’s not that big of an issue… especially since reads aren’t being blocked by this anyway.  This approach also prevents the creation of 2 ISessionFactory instances for the same tenant.  Well, unless i missed a bug here :p

Now, once the ISessionFactory instance is created, we create a new Dictionary based on the contents of the old one and then we add the new ISessionFactory instance to it.  After that, we replace the sessionFactories references with the new dictionary and from that point on, every read will use the new dictionary instance.  During this entire operation, no read operation was impacted negatively. 

Now lets take a look at the other write operation, removing an ISessionFactory instance from the dictionary:

        public void RemoveSessionFactoryForTenant(Guid tenantId)

        {

            if (!sessionFactories.ContainsKey(tenantId))

            {

                return;

            }

 

            lock (writeLock)

            {

                if (!sessionFactories.ContainsKey(tenantId))

                {

                    return;

                }

 

                var sessionFactory = sessionFactories[tenantId];

                var newDictionary = new Dictionary<Guid, ISessionFactory>(sessionFactories);

                newDictionary.Remove(tenantId);

                sessionFactories = newDictionary;

 

                sessionFactory.Dispose();

            }

 

The first if-check, which happens outside of the lock is a bug that i missed but that was pointed out in the comments of the original post.  If CreateSessionFactoryForCurrentTenant and RemoveSessionFactoryForTenant would execute concurrently for the same tenant, it’s possible that the ISessionFactory instance of that tenant is never removed from the dictionary (and also never disposed of…) since the check happens outside of the lock and could be executed before the ISessionFactory of the tenant was added to the dictionary.  In that case, the ISessionFactory instance would stay in the dictionary as long as the application stays up.  This is definitely a race condition that you want to avoid in every other situation though in this case, the odds that we’re simultaneously adding and removing the same tenant are slim to none.  Nevertheless, i don’t want to be accused of promoting race conditions so we’ll make the change anyway :)

        public void RemoveSessionFactoryForTenant(Guid tenantId)

        {

            lock (writeLock)

            {

                if (!sessionFactories.ContainsKey(tenantId))

                {

                    return;

                }

 

                var sessionFactory = sessionFactories[tenantId];

                var newDictionary = new Dictionary<Guid, ISessionFactory>(sessionFactories);

                newDictionary.Remove(tenantId);

                sessionFactories = newDictionary;

 

                sessionFactory.Dispose();

            }

        }

 

Now, as you can see we once again create a new dictionary based on the previous one, then remove the ISessionFactory instance for the current tenant and then we overwrite the sessionFactories instance once again.

Finally, there’s the read operation that i specifically didn’t want suffering from locking overhead:

        public ISession CreateSessionForCurrentTenant()

        {

            var tenantId = tenantContext.CurrentTenantId;

 

            if (!sessionFactories.ContainsKey(tenantId))

            {

                CreateSessionFactoryForCurrentTenant();

            }

 

            return sessionFactories[tenantId].OpenSession();

        }

 

The only time this code will block is when a new ISessionFactory for the current tenant needs to be created.  Luckily, that only happens once for each tenant.  As i mentioned earlier in the post, using this pattern doesn’t guarantee that successive reads within the same logical operation are thread safe, so there is a bug in here.  If a tenant already has an ISessionFactory instance, it’s possible that the RemoveSessionFactoryForTenant method has been executed between the if-check and accessing the ISessionFactory based on the tenantId.  In that particular scenario, the ISessionFactory instance is no longer in the dictionary which will cause this code to throw an exception.

That’s a bug that i don’t feel like fixing though… Once a tenant has been removed, they are no longer a paying customer.  If they are no longer paying for the software, there is no reason whatsoever why i should care about any possible exceptions they could get while running the software :)

Seriously though, if the RemoveSessionFactoryForTenant method is called, users of that tenant won’t even have access to the system anymore so it’s really a non-issue.

Anyways, i think i’ve covered the implementation in more detail than you probably cared for.  So, any thoughts? Are there still issues that i haven’t thought of? Is there another approach that you would use for this specific scenario?


Categories: Programming

New Tools for Prioritizing Backlogs Available

Mike Cohn's Blog - Mon, 03/08/2010 - 15:37

We’ve added two new tools for prioritizing a product backlog: Theme Screening and Theme Scoring. Each of these is a lightweight way of comparing product backlog items to one another.

Theme Scoring

You can use theme scoring to compare user stories or entire projects against one another. In this technique you identify a set of criteria that will be important in prioritizing. Each item is assessed on a relative 1-5 scale against each criterion and the priorities are determined.
theme scoring

Theme Screening

Like theme scoring and relative weighting, this technique can be used to prioritize user stories or projects against one another. The simplest of the three prioritization techniques, theme screening starts with you identifying a baseline item. Each other item to be prioritized is compared to the baseline item for a set of factors that will determine priorities.
theme screening