Here's my take on it, which I wanted to post up.
Do regular code reviews. Require that future code be written to take past reviews into account;. If you can, build in some kind of penalty or incentive mechanism for the offshore team so that repeating the same code review problems again (and again (and again)) don't eat your in-house developers' morale for lunch.
Many offshore teams will take a very literal interpretation of any specification, and when something isn't specified, they'll make a quick best-guess to fill the gap instead of spending time coming back and asking what it should be. By the terms of most agreements, they've arguably built what you asked them to. I have no idea if this tactic is for speed or for expanding the scope - whether it's malicious or simply inefficient - but spending more time on requirements is key. Having a business analyst to do this is hugely useful. Discussing the requirements with offshore verbally helps, to make sure everyone's on the same page. Discussing the requirements face-to-face with a copy open on a laptop - to mutually alter them to clarify misunderstandings - is an enormously useful tool.
Offshore groups tend to cut and paste things that I would have never, ever considered. I've seen 19 copies of a method called "foramtZipCode()" in the same application, all of which did the same thing, some of which did it in different ways. It's hard to verbally pin down an acceptance criteria on code reuse, but PMD CPD gives you a grasp of how many lines of code have been copied, and CLOC gives you a metric for total lines of code; you can easily set an acceptance percentage there.
Laying out a package tree sometimes helps, and sometimes hurts. Some offshore teams basically have a "model application" they'll adapt to your project, some don't. If they have a model app, they'll already have a package tree in mind. If they don't, code review this early and often.
Unit tests need to be specifically required. What types of classes do they need to test? Every method? Every static method? Every utility class or validator? Controllers? Every method that's not a getter or setter? Often, the offshore teams don't have a grasp of why unit testing is helpful; their experience isn't long term maintenance of a project, it's short term building of an initial application.
Some of the developers offshore will not have a computer science degree, and have never worked outside of the group they're in, so they've never had time to absorb the best-practices of software engineering. The only way to avoid this is to pick an offshore company carefully.
The UIs are often wildly inconsistent, and build as state-of-the-art 1997 HTML. I've solved this in the past with JSP custom tags, and required they use the tags instead of HTML. This has been true on every offshored project I've seen, albeit offshore teams specialized in UIs instead of domain-based-knowledge would be ideal here.
The English spelling and grammar are off. Sometimes it's gone through a spellchecker, sometimes an idiom is used (or misused), and sometimes they speak British English, when your client base is American. At least once per project, someone will put an exclamation mark (or three) after an error message. (The first name is required!!!) Make sure someone onshore has a grasp on all of the messages that are client-visible, or it will be obvious that it's been offshored. Budget time for that onshore person to review the text, and if you'll never need internationalization, avoid it, as it adds a layer of obfustication to reviewing the text.
Make sure your objectives match. In cases where the development shop is fixed-time (not fixed-cash or fixed-features, but driven by a release date), a fixed-bid contractor isn't a natural match. If your project is very date-driven, offshoring on a tight schedule will most likely be a rough match.
Be careful of optimistic estimates, just like you would with any contractor. Do not base sales numbers off of estimates you've accepted at face value; ask for explanations of those estimates, and find where the schedule might break down before pushing the schedule outside of your group.
Never, ever have the same offshore company responsible for requirements, development, and testing. Either keep some work onshore, or make development separate; the wolf shouldn't guard the hen-house, so to speak.
If you have the opportunity, build the first release of the software onshore, then supplement offshore teams to speed up development. This gives you a template, where you can then say "match this style", instead of having to code review afterwards. This can be an enormous quality improvement step.
Have a mutually-available source control/version control server.
Have regular check-ins and checkpoints, much as you would with any other external contractor.
As someone else pointed out, avoid the them-and-us mentality. I've had great experiences integrated alongside of foreign developers. I'm still not fond of offshore group's managers, as their (unstated) goal is often to finish quickly instead of finishing well.
And that went much longer than I thought it might, but I think the gist is there. On the plus side, the offshore teams really do produce code very quickly, but in most cases, you need far more controls on them to make sure you're hitting both the right functionality and maintenance requirements.