This paper presents a design principle end-to-end argument and explains its importance in helping guide placement of functions among the modules of a distributed computer system. End-to-end argument states that the function in question can completely and correctly be implemented only with the knowledge and help of the application standing at the end points of the communication system. Providing that questioned function as a feature of communication system itself is not possible. There are two reasons to implement certain function end-to-end: 1) The service provided by the low level subsystem may be shared by a number of applications, and those that don't need the service will pay for the unnecessary overhead; 2) The low-level subsystem may not have as much information as higher level to do certain jobs efficiently. The authors use many examples to support this point. In the "careful file transfer" example, two approaches: "step by step check and retry" and "end-to-end check and retry" are carefully compared under different assumptions. It is concluded that the "end-to-end check and retry" is much more efficient than reinforcing each of the file transfer steps to guarantee the correctness and furthermore, placing functions at low levels is just an engineering tradeoff based on performance. Other examples such as delivery guarantees, secure transmission of data, duplicate message suppression, guaranteeing FIFO message delivery and transaction management are provided to further illustrate the end-to-end argument. While the main guideline is to put more function into the end system, it is important to correctly identify the "ends" of the system, which requires subtlety of application requirement analysis. An example of voice communication of different requirements is given to illustrate ends identifying and that the end-to-end arguments should be used as a guideline carefully rather than as an absolute rule. In the end, the authors discuss the history and application background of end-to-end arguments.