Information security, software assurance, and program performance all crucially depend on our ability to reason formally about possible program behaviors. Determining if a particular function will reveal a password, avoiding code that crashes embedded devices, and applying transformations that provide an order-of-magnitude speed up, are all tasks that require treating the code as an object of analysis on which we can perform meaning-preserving transformations. This talk will overview the foundations of program analysis; which started 50 years ago with data flow analysis and abstract interpretation. Advancing through the years, we will touch on some key techniques and discuss their practical implications. We will conclude with a look at unsound techniques, explain the sources of unsoundness and what can be done to mitigate it.