Plan plan plan!!! The greatest cost of automated tests is maintenance, and as the project grows it gets more and more difficult to do if the proper amount of planning wasn't utilized during the initial design of the test project. Whether an application is still under development or not, the first thing you should do (if you don't have one already) is write a testing framework that will interpret to webdriver calls. If you have driver doing anything in your actual test method, it's bad design and should be fixed immediately. For all of my projects, I actually have two frameworks; a very abstract base framework that can be reused across all projects, and then each project has a more specific framework that will only be used for that project. I've seen some designs that have 4-7 layers but I've never found a need to contain all navigation in a specific layer and ideas like that. My test classes initialize the driver in the constructor and that's it. The only thing webdriver is doing in the less abstract class is finding the mapped elements and navigating. Everything else is accomplished in the base framework, so you can get a lot of the work out of the way before the first page is even deployed.
The nice thing about having a base framework is it keeps your tests from getting messy and hard to read, because if you're just using Webdriver, you've got two options:
Dynamically find each element with the driver or create an element that is specific to one browser, i.e. FirefoxElement or ChromeElement. If you build a framework, you can type these elements specifically and give different functionality to the different types of elements. For example, ButtonElement, InputElement, SelectElement, and so on. These elements shouldn't inherit from IWebElement, but instead contain an IWebElement element (the "favor composition over inheritance" principle); besides, inheriting from IWebElement is a giant pain. The constructor of such could either take an IWebElement or an ISearchContext, so you could either find the element with the driver or pass in a By. Frameworks will also allow for greater code reuse. The upfront cost is high but is definitely worth it in the long; you'll write new tests quicker, and the framework itself will require essentially no maintenance.
The second framework is generally the pages you are going to map, the test classes that should correspond to each page class, and an inheritance of "testers" that perform all the functions that get performed in the tests. For example, LoginPage class maps to your login page, which gets tested by LoginPageTests, which inherits from LoginPageTester that contains all page-specific functions, which inherits from BaseTester, which contains functions that will be reused across many different classes of tests. If you're going to have a lot of tests that have to cross multiple pages to complete (not counting Setups of course) then making UtilityTesters that aren't part of the inheritance structure is a great idea. Put all of the functions that the tests will use into these, and give the BaseTester class a reference to all of them. This will make all the functions available to all test classes without making the tests cluttered and difficult to read.
These are just some suggestions for how you can set up your framework, but the one thing that a lot of testers ignore is the idea that tests will require maintenance forever, and it won't always be done by the same person who wrote the tests. Therefore, a lot of your time when first starting to test a new or future application should be spent planning and engineering a great framework that will keep maintenance simple and intuitive, and when writing the tests, above all make their intent easy to understand by naming variables and functions well.