Q: Create Child Record if a lookup on a Parent Object Changes I'm pretty close to being maxed out on workflows for my custom objects. So I decided to see if apex classes could help, I'm still currently trying to learn how to write triggers and classes so Im pretty lost.
I need to be able to create a related record when a lookup field is changed.
My object, storages has a related list of packages.
When a field on storage is change, it should create a new package.
Also for later, if the field changes again with the biohazard field = false we need to capture the time/date it happens on a date/time field on that said package record.
I know I can capture some of this with process builder but like I mentioned im very close to maxing out and would like an alternative or something to work off of. I plan on using process builder to delete this records when storage is in a certain pick list option.
EDIT:
my class I have so far for this. It works but it is creating 2 records at a time. Should i not create a list since I only want one record to be created? when i remove the list and just to storage__c storage and remove the first for it gives me an error "caused by: System.NullPointerException: Attempt to de-reference a null object: 
public with sharing class newPackagePPP {

 //create package record  when storage field changes 
 public static void createPPP(Map < Id, storage__c > newStorages,
  Map < Id, storage__c > oldStorages, list < storage__c > storages) {
  List < Packages__c > P = new List < Packages__c > ();
  for (Id id: newStorages.keyset()) {
   for (Storage__c storage: storages) {
    if ((newStorages.get(id).BIO__c = TRUE ||
      newStorages.get(id).BioSent__c == 'Confirmed') && .
     (oldStorages.get(id).BIO__c = False ||
      oldStorages.get(id).BioSent__c != 'Confirmed')) {
     P.add(new Packages__c(Storage__c = storage.id, PPP__c ==
      TRUE));
     insert P;
    }
   }
  }
 }
}

Im sure there is a much simpler way to do this but I took a shot at it. 
Edit:
  public with sharing class newPackagePPP {

  //create package record  when storage field changes 
  public static void createPPP(
  Map < Id, storage__c > oldStorages,  List<storage__c> storages) {
  list<Packages__c>  P = new list<Packages__c> ();
   for(Storage__c storage :storages)  {
  if ((Storage.BIO__c = TRUE ||
  Storage.BioSent__c == 'Confirmed') && .
 (oldStorages.get(storage.id).BIO__c = False ||
  oldStorages.get(storage.id).BioSent__c != 'Confirmed')) {
  P.add(new package__c(storage__c = storage.id, PPP__c = TRUE));
 insert P;
    }
   }
 }
 }

}
My trigger:
    else if (Trigger.IsAfter && Trigger.isUpdate){
    StorageTriggerHandler.handleAfterUpdate(Trigger.NewMap, 
    Trigger.OldMap);
    }

trigger handler includes this:
    Packages.createPPP(oldStorages,newStorages.values());


A: Original Version
I see two logic errors here, although I'd think you would get an exception before seeing multiple objects inserted. 
You're iterating over the same objects twice:
for (Id id: newStorages.keyset()) {
   for (Storage__c storage: storages) {

I'm assuming this method is called from a trigger's update event as createPPP(Trigger.newMap, Trigger.oldMap, Trigger.new); 
In that case, you are iterating over the set of updated Ids, but then within that loop iterating over the list of new records again. There's no need to do that and it'll result in multiplicative insertions.
You can remove the inner loop entirely, since you do not refer to its loop variable storage.
Secondly, you're performing DML in a loop:
 insert P;

This would result in inserting multiple objects if it didn't throw an exception first for trying to insert objects that already have a populated Id value. You should move your insert outside the loop as the final statement executed in the method.
If you're getting NullPointerException, I wonder if you're calling this from both insert and update trigger events. insert events by nature don't have an oldMap. 
Revised Version
Taking in some of the material from comments.
Your new version is executing DML for each trigger object and will result in limits exceptions at volume. Since it contains no iteration, it cannot insert two records, meaning the issue lies elsewhere - like this method being called twice in a transaction for the same record.
You mention that your handler calls no fewer than 14 functions for this trigger. My suspicion is that one of the other functions fires a new update DML on the Storage__c object, resulting in trigger recursion and execution of your class a second time on the same objects. To determine whether this is the case, you may want to include System.debug() statements in your handler to see how many times it's called when you update a record.
If you do have trigger recursion, you'll need to ensure that your method only executes once for any given object. A static Set<Id> variable tracking all of the Storage__c records already acted upon in a transaction is one way to approach this, but given the apparent complexity of your trigger architecture I am leery of prescribing an exact approach for you. If you have access to an architect or lead developer on your team, you may wish to work with them to ensure your code and your recursion prevention strategy works effectively with this complex trigger and handler stack.
